微信小程序上传文件后端接口C实现

微信请求函数

const uploadTask =  wx.uploadFile({
            url: 'http://ip地址/echo.cgi',
            filePath: that.data.cutImgUrl,
            name: 'file',
            formData: {
        'user': '/home/nginx/html/image/logo'
            },
            success: function (res) {
                var data = res.data;
                console.log(" res.data" + res.data);
                that.setData({ display: "hied" });  
            }
        })

接下来我们看下前端传递的数据是怎样的格式

<title>FastCGI echo</title><h1>FastCGI echo</h1>
Request number 1,  Process ID: 3520<p>
Standard input:<br>
<pre>
----------------------------753651501054604630337800
Content-Disposition: form-data; name="user"

/home/nginx/html/image/logo
----------------------------753651501054604630337800
Content-Disposition: form-data; name="file"; filename="touristappid.o6zAJs5MrQFix1tcXqPxSNHp7TYM.Qqpwv0q7GmO074368faff93b1673d56dd872406e5d09.jpg"
Content-Type: image/jpeg

数据区
----------------------------753651501054604630337800--

</pre><p>
Request environment:<br>
<pre>
FCGI_ROLE=RESPONDER
SCRIPT_FILENAME=fcgi/echo.cgi
QUERY_STRING=
REQUEST_METHOD=POST
CONTENT_TYPE=multipart/form-data; boundary=--------------------------753651501054604630337800
CONTENT_LENGTH=10532
SCRIPT_NAME=/echo.cgi
REQUEST_URI=/echo.cgi
DOCUMENT_URI=/echo.cgi
DOCUMENT_ROOT=
SERVER_PROTOCOL=HTTP/1.1
REQUEST_SCHEME=http
GATEWAY_INTERFACE=CGI/1.1
SERVER_SOFTWARE=nginx/1.13.5
REMOTE_ADDR=168.160.111.53
REMOTE_PORT=55809
SERVER_ADDR=
SERVER_PORT=
SERVER_NAME=localhost
REDIRECT_STATUS=200
HTTP_REFERER=https://servicewechat.com/touristappid/devtools/page-frame.html
HTTP_HOST=
HTTP_CONTENT_TYPE=multipart/form-data; boundary=--------------------------753651501054604630337800
HTTP_CONTENT_LENGTH=10532
HTTP_CONNECTION=close
</pre><p>
Initial environment:<br>
<pre>
TOMCAT_HOME=/opt/apache-tomcat-8.0.46
XDG_SESSION_ID=1
ANDROID_HOME=
TERM=xterm
SHELL=/bin/bash
SSH_CLIENT=
GRADLE_HOME=
SSH_TTY=/dev/pts/10
ANT_HOME=/usr/share/ant/
NDK_HOME=
USER=
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:
PYTHON_LIBS=/usr/lib/python2.7
PYTHON_CFLAGS=/usr/include
MAIL=
PATH=
PWD=
JAVA_HOME=
ANDROID_SDK=
LANG=zh_CN.UTF-8
SHLVL=2
HOME=
LANGUAGE=zh_CN:zh
LOGNAME=
CLASSPATH=
SSH_CONNECTION=
LESSOPEN=| /usr/bin/lesspipe %s
ANDROID_NDK=
XDG_RUNTIME_DIR=/run/user/1000
LESSCLOSE=/usr/bin/lesspipe %s %s
</pre><p>

我需要的是文件的名字

filename="touristappid.o6zAJs5MrQFix1tcXqPxSNHp7TYM.Qqpwv0q7GmO074368faff93b1673d56dd872406e5d09.jpg"

文件存放路径/home/nginx/html/image/logo
文件具体内容也就是data这块。

接下来来看看后端接口


#include <fcgi_stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <dirent.h>
#include <pthread.h>
#include <fcgiapp.h>
#include <sys/time.h>  
#include <time.h>
#include <mysql/mysql.h>
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)  //解决获取线程ID的问题

#include "qsi.h"
#include "cJSON.h"


#define TEMP_BUF_MAX_LEN 51200
#define FILE_NAME_LEN 256
#define USER_NAME_LEN 256

#define THREAD_COUNT 10     //线程数
static int counts[THREAD_COUNT];




//FCGX_Request request;
static pthread_mutex_t pthread_mutex = PTHREAD_MUTEX_INITIALIZER;
int MUX_THREAD_TEST = 0;

char upload_error[256] = "/home/log/upload_error.log";  //日志文件目录

/*
    功能:获取已经上传过后的文件列表,(文件名,MD5值)
    参数:
        filePath    文件路径
        folderPath  文件夹路径
    返回值:成功0 失败返回-1
*/
int getChunkList(char *filePath, char *folderPath, FCGX_Request request)
{
    /*
        将目录下的所用文件组成一个文件列表,返回到前端 
    */
    char fullpath[256];
    memset(fullpath, 0x00, 256);
    sprintf(fullpath, "%s/%s", filePath, folderPath);
        
    //创建一个文件jSON对象
    cJSON * allfile = cJSON_CreateArray();    
    cJSON * fullnode= cJSON_CreateObject();

    char * out = NULL;
    if(NULL == fullpath)
    {       
        FCGX_FPrintF(request.out,"%s", "enter path null!\n");
        return -1;
    }

    DIR *dirp = opendir(fullpath);  //打开目录
    if(dirp == NULL)
    {
        FCGX_FPrintF(request.out,"%s", "open dir err");
        return -1;
    }
        
    struct dirent * dentp = NULL;
    
    //读取目录下文件
    while((dentp = readdir(dirp)))
    {           
        if((strcmp(".", dentp->d_name)) == 0 || (strcmp("..", dentp->d_name)) == 0)
        {
            continue;
        }
        
        //不是目录文件
        if(dentp->d_type == DT_REG)
        {           
            struct stat filestruct;           
            char newfilepath[256];
            sprintf(newfilepath, "%s/%s", fullpath, dentp->d_name);
            stat(newfilepath, &filestruct);
            
            //创建一个jSON对象
            cJSON *chunkList = NULL;            
            if(strcmp(folderPath, dentp->d_name ) == 0)
            {
                chunkList = cJSON_CreateObject();
                cJSON_AddStringToObject(chunkList, "filename", dentp->d_name);
                FCGX_FPrintF(request.out,"%s", cJSON_Print(chunkList));
                cJSON_Delete(chunkList);                
                return 0;
            }
            else
            {
                cJSON_AddItemToArray(allfile, chunkList = cJSON_CreateObject());
                cJSON_AddStringToObject(chunkList, "filename", dentp->d_name); 
                cJSON_AddNumberToObject(chunkList, "indexof", atoi(dentp->d_name));                 
            }                                                                                     
        }   
        else
        {
            return 0;               
        }                  
    }   
    cJSON_AddItemToObject(fullnode, "chunkList", allfile);          
    FCGX_FPrintF(request.out,"%s", cJSON_Print(fullnode));
    cJSON_Delete(fullnode);
    
    return 0;   
}

/*
    判断文件夹是否存在,文件夹不存在创建文件夹,
    文件夹存在判断里面的内容是否正确,
    如果文件存在就组织文件的json格式返回给前端,
    如果文件不存在就返回目录下的所用文件返回给前端
*/

/*
    功能:判断文件夹是否存在,不存在创建一个文件夹
    
    参数:
        filepath   文件夹路径
        filename   文件夹名称
    
    返回值:成功返回0 失败返回-1
*/

int folderIsExit(char *filepath, char *filename, FCGX_Request request)
{       
    char fullpath[256] = {0};
    memset(fullpath, 0x00, sizeof(fullpath));
    char new_file_name[256] = {0};
    sprintf(new_file_name, "%s", filename);
    
    sprintf(fullpath, "%s/%s", filepath, filename);
    char errlog[256] = {0};
    FCGI_FILE *fp = NULL;      
    
    if(access(fullpath, W_OK) == 0)
    {       
        //目录存在且有写的权限    遍历目录下所有文件组织文件链表
        if(getChunkList(filepath, filename, request))
        {
            pthread_mutex_lock(&pthread_mutex);            
            sprintf(errlog, "error: getChunkList function error!\r\n");                        
            fp = FCGI_fopen(upload_error,"ab+" );
            FCGI_fwrite(errlog, 1, strlen(errlog), fp);
            FCGI_fclose(fp);
            memset(errlog, 0x00, strlen(errlog));       
            pthread_mutex_unlock(&pthread_mutex);           
        }           
    }
    else
    {
        //目录不存在或没有写的权限  
         if(mkdir(fullpath, S_IRWXU|S_IRWXG|S_IRWXO) == -1) //0777
        {
            pthread_mutex_lock(&pthread_mutex);            
            sprintf(errlog, "error:dir create error occurred mkdir function error!\r\n");                        
            fp = FCGI_fopen(upload_error,"ab+" );
            FCGI_fwrite(errlog, 1, strlen(errlog), fp);
            FCGI_fclose(fp);
            memset(errlog, 0x00, strlen(errlog));       
            pthread_mutex_unlock(&pthread_mutex);   
            FCGX_FPrintF(request.out, "create dir error occurred!");
            FCGX_FFlush(request.out);
        }
        else
        {   
            pthread_mutex_lock(&pthread_mutex);           
            cJSON *fileinfo = cJSON_CreateObject();
            cJSON_AddStringToObject(fileinfo, "fileName", new_file_name);       
            FCGX_FPrintF(request.out,"%s", cJSON_Print(fileinfo));
            cJSON_Delete(fileinfo); 
            pthread_mutex_unlock(&pthread_mutex);   
            FCGX_FFlush(request.out);       
        }   
    }   
    FCGX_FFlush(request.in);
    return 0;   
}                            


/*
    功能:在指定长度的字符串中查找字符串
    参数:
        (in)    full_data            查找字符串
        (in)    full_data_len        查找长度长度
        (in)    substr               要查找的字符串
    返回值:成功返回要查找的字符串的初始位置 失败返回NULL   
*/
char* memstr(char* full_data, int full_data_len, char* substr)
{
    if (full_data == NULL || full_data_len <= 0 || substr == NULL)
    {
        return NULL;
    }

    if (*substr == '\0')
    {
        return NULL;
    }

    int sublen = strlen(substr);
    char* cur = full_data;
    int last_possible = full_data_len - sublen + 1;
    int i = 0;
    for (i = 0; i < last_possible; i++)
    {
        if (*cur == *substr)
        {
            if (memcmp(cur, substr, sublen) == 0)
            {
                // found
                return cur;
            }

        }
        cur++;
    }
    return NULL;
}


/**
 * @brief  解析上传的post数据 保存到本地临时路径
 *         同时得到文件上传者、文件名称、文件大小
 *
 * @param len       (in)    post数据的长度
 * @param user      (out)   文件上传者
 * @param file_name (out)   文件的文件名
 * @param p_size    (out)   文件大小
 *
 * @returns
 *          0 succ, -1 fail
 */
int recv_save_file(long len, char *user, char *filename, long *p_size,
                   FCGX_Request request, char *filepath)
{   
    char log[526] = {0};
    int ret = 0;
    char *file_buf = NULL;
    char *begin = NULL;
    char *p = NULL, *q = NULL, *k = NULL;
    
     int fd = 0;
    int ret2 = 0;
    
    char *heto = NULL;
    char *to = NULL;
         
    int totallen = 0;
    char * toen = NULL;
    
    int num = 0;
    
    char errlog[256] = {0};
     
    int FragmentName = 0;   
    char FragmentNum[50] = {0};
    
    char FPATH[256] = {0};
    
    char content_text[TEMP_BUF_MAX_LEN] = {0};      //文件头部信息
    char boundary[TEMP_BUF_MAX_LEN] = {0};          //分界线信息
    FCGI_FILE *fp = NULL;

#if 0
    if(!opendir(filepath))
     {
        pthread_mutex_lock(&pthread_mutex);        
        sprintf(errlog, "error:upload file dir is NULL!\r\n");                  
        fp = FCGI_fopen(upload_error, "ab+");
        FCGI_fwrite(errlog, 1, strlen(errlog), fp);
        FCGI_fclose(fp);
        memset(errlog, 0x00, strlen(errlog));       
        pthread_mutex_unlock(&pthread_mutex);   
        ret = -1;
        goto END;       
     }
#endif

    //==========> 开辟存放文件的 内存 <===========
    file_buf = (char *)malloc(len);
    memset(file_buf,0x00, sizeof(file_buf));
    
    if (file_buf == NULL)
    {
        goto END;
    }
        
    for(int i=0; i<len; i++)   //读取标准输入
    {
        file_buf[i] = FCGX_GetChar(request.in);         
    }
                
    begin = file_buf;    //内存起点
    p = begin;

   
    p = strstr(begin, "\r\n");
    if (p == NULL)
    {       
        ret = -1;
        goto END;
    }

        
    //拷贝分界线
    strncpy(boundary, begin, p - begin);
    boundary[p-begin] = '\0';   //字符串结束符
   
       
    p += 2;//\r\n
    //已经处理了p-begin的长度
    len -= (p-begin);
   
    begin = p;
   
    p = strstr(begin, "\r\n");
    if(p == NULL)
    {
        
        ret = -1;
       goto END;
    }
    strncpy(content_text, begin, p-begin);              //拷贝Content-Disposition这一行内容
    content_text[p-begin] = '\0';

    p += 2;//\r\n
    len -= (p-begin);

    //========================================获取文件上传者
    //Content-Disposition: form-data; user="mike"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n                                   ↑
    q = begin;
    q = strstr(begin, "name=");
    

    //Content-Disposition: form-data; user="mike"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n                                     ↑
    q += strlen("name=");
    q++;    //跳过第一个"

    //Content-Disposition: form-data; user="mike"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n                                          ↑
    k = strchr(q, '"');
    strncpy(user, q, k-q);  //拷贝用户名
    user[k-q] = '\0';
    
    //FCGI_printf("user = %s\n" , user);
    //去掉一个字符串两边的空白字符
    //trim_space(user);   //util_cgi.h

    //========================================获取文件名字
    //"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n
    //   ↑
    
    begin = k;
    q = begin;
    q = strstr(begin, "filename=");

    //"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n
    //             ↑
    q += strlen("filename=");
    q++;    //跳过第一个"

    //"; filename="xxx.jpg"; md5="xxxx"; size=10240\r\n                    ↑
    k = strchr(q, '"');
    strncpy(filename, q, k-q);  //拷贝文件名
    filename[k-q] = '\0';
        
    begin = p;
    p = strstr(begin, "\r\n");
    p += 4;//\r\n\r\n
    len -= (p-begin);

    //下面才是文件的真正内容   
    begin = p;
    
    //find file's end
    p = memstr(begin, len, boundary);//util_cgi.h, 找文件结尾
    if (p == NULL)
    {        
        ret = -1;
        goto END;
    }
    else
    {
        p = p - 2;//\r\n
    }
    
    //解析获的文件编号 获取总片数
     heto = p + 2;
     to = NULL;
     to = strstr(heto, "\r\n");
     to += 4;
     
     totallen = 0;
     toen = strstr(to, "\r\n");
         
     toen += 4;
     to = toen;
     toen = strstr(to, "\r\n");
     
     totallen = toen - to;
         
     memset(FragmentNum, 0x00, sizeof(FragmentNum));
     strncpy(FragmentNum, to, toen - to);               //获取片段总数
    
     num = atoi(FragmentNum);
     toen+= 2;
     to = toen;
     
     toen = strstr(to, "\r\n");
     toen += 2;
     
     to = toen;
     toen = strstr(to, "\r\n");
     
     toen += 4;
     to = toen;
     toen = strstr(to, "\r\n");
         
     memset(FragmentNum, 0x00, sizeof(FragmentNum));
     strncpy(FragmentNum, to, toen - to);  
     FragmentName = atoi(FragmentNum);
        
      sprintf(FPATH, "%s/%s", filepath, FragmentNum);
          
      fd = open(FPATH, O_CREAT|O_WRONLY, 0644);
      
      if (fd < 0)
      {
          ret = -1;
          goto END;
      }
    
      //ftruncate会将参数fd指定的文件大小改为参数length指定的大小
      ftruncate(fd, (p-begin));
      write(fd, begin, (p-begin));
      close(fd);
    
END:
    free(file_buf);
    file_buf = NULL;
    FCGX_FFlush(request.in);
    memset(log, 0x00, strlen(log));
    memset(FragmentNum, 0x00, strlen(FragmentNum));
    memset(FPATH, 0x00, strlen(FPATH));
    memset(content_text, 0x00, strlen(content_text));
    memset(boundary, 0x00, strlen(boundary));

    if(begin)
    {
       begin = NULL;    
    }

    if(p)
    {
        p = NULL;   
    }
    
    if(q)
    {
        q = NULL;   
    }
    
    if(k)
    {
       k = NULL;    
    }
    
    if(heto)
    {
        heto = NULL;    
    }

    if(to)
    {
       to = NULL;   
    }
        
    if(toen)
    {
        toen = NULL;
    }

    
    
    
    
    return ret;
}

/*
    功能:获取文件大小
    参数:
        path  文件路径
    返回值:成功返回文件大小 失败返回-1
*/
int getfilesize(char *path)
{
    FILE *pf = fopen(path, "rb");
    if (pf==NULL)
    {
        return -1;
    } 
    else
    {
        fseek(pf, 0, SEEK_END);
        int size = ftell(pf);
        fclose(pf);
        return size;
    }
}



/*
    功能:将多个文件合并为一个文件, 同时删除片段文件
    参数:
        part_f_path  片段文件路径
        filepath     合并后文件路径
        filename     合并后文件名
    返回值: 成功返回 0  失败返回-1

*/
int  merge(char *part_f_path , int part_num, char *filepath, char *filename, FCGX_Request request)
{
    char newpath[part_num][600];                //代表路径  
    for(int i=0; i<part_num; i++)
    {
        memset(newpath[i], 0x00, sizeof(newpath[i]));           
    }
    
    char fullpath[500] = {0};
    memset(fullpath, 0x00, sizeof(fullpath));
    sprintf(fullpath, "%s/%s",filepath, filename);
    for (int i = 0; i < part_num; i++)
    {
        sprintf(newpath[i], "%s/%d", part_f_path, i);
    }   
        
    FCGI_FILE *pfw = fopen(fullpath, "wb");
    for (int i = 0; i < part_num; i++)
    {
        int length = getfilesize(newpath[i]);
        if (length != -1)
        {
            FCGI_FILE *pfr = FCGI_fopen(newpath[i], "rb"); //读取
            for (int j = 0; j < length; j++)
            {
                char ch = fgetc(pfr);
                fputc(ch, pfw);                 //写入
            }
            FCGI_fclose(pfr);
            remove(newpath[i]);
        }
    }
    FCGI_fclose(pfw);
    return 0;
}


int wx_recv_save_file(long len, char *user, char *filepath, char *filename,
                      long *p_size, FCGX_Request request)
{       
    char fullpath[256]  = {0};
    char newfile[256]   = {0};
    char file_md5[256]  = {0};
    char newname[256]   = {0};
    int ret = 0;
    int num = 1;
    char head_name[256] = {0};
    char *end_name = NULL;
         
    char *file_buf = NULL;
    char *begin    = NULL;
    char *p = NULL, *q = NULL, *k = NULL;
    
    FCGI_FILE * fd = NULL;
    int ret2 = 0;
    
    char *heto = NULL;
    char *to = NULL;
         
    int totallen = 0;
    char * toen = NULL;
    
    char *filepath_head = NULL, *filepath_end = NULL;
    char path[256] = {0};

     
    int FragmentName = 0;   
    char FragmentNum[50] = {0}; 
    char FPATH[256] = {0};
    
    char *f = NULL, *e = NULL; 
        
    char content_text[TEMP_BUF_MAX_LEN] = {0}; //文件头部信息
    char boundary[TEMP_BUF_MAX_LEN] = {0};     //分界线信息
    char errlog[256] = {0};
    FCGI_FILE *fp = NULL;
    
 #if 0   
    pthread_mutex_lock(&pthread_mutex);   
    sprintf(errlog, "debug info: write funcition start !\r\n");                  
    fp = FCGI_fopen(upload_error,"ab+" );
    FCGI_fwrite(errlog, 1, strlen(errlog), fp);
    FCGI_fclose(fp);
    memset(errlog, 0x00, strlen(errlog));       
    pthread_mutex_unlock(&pthread_mutex);   
 #endif   
   
    //==========> 开辟存放文件的 内存 <===========
    file_buf = (char *)malloc(len);
    memset(file_buf, 0x00, len);
    
    if (file_buf == NULL)
    {
        goto END;
    } 
       
    if (file_buf == NULL)
    {
        goto END;
    }
    
    pthread_mutex_lock(&pthread_mutex);     
    for(int i = 0; i < len; i++)   //读取标准输入
    {
        file_buf[i] = FCGX_GetChar(request.in);         
    }
    pthread_mutex_unlock(&pthread_mutex);
    
    //===========> 开始处理前端发送过来的post数据格式 <============
    begin = file_buf;    //内存起点
    p = begin;

    
    p = strstr(begin, "\r\n");
    if (p == NULL)
    {
        ret = -1;
        goto END;
    }

    //拷贝分界线
    strncpy(boundary, begin, p-begin);
    boundary[p-begin] = '\0';   //字符串结束符 拿到分界线 
        
    p += 2;//\r\n
    //已经处理了p-begin的长度
    len -= (p-begin);

    begin = p;

    p = strstr(begin, "\r\n");
    if(p == NULL)
    {
       ret = -1;
       goto END;
    }
    
    strncpy(content_text, begin, p-begin); //拷贝Content-Disposition这一行内容
    content_text[p-begin] = '\0';

    p += 2; //\r\n
    len -= (p - begin);
    /*******************************/
    filepath_head = p + 2;
    filepath_end = strstr(filepath_head, "\r\n");
    strncpy(path, filepath_head, filepath_end - filepath_head);
    if(NULL == filepath)
    {
        filepath = path;
    }   
                               ↑
    q = begin;
    q = strstr(begin, "name=");
    

    q += strlen("name=");
    q++;    //跳过第一个"

    k = strchr(q, '"');
    strncpy(user, q, k-q);  //拷贝用户名
    user[k-q] = '\0';
    
    
    begin = k;
    q = begin;
    q = strstr(begin, "filename=");

                ↑
    q += strlen("filename=");
    q++;    //跳过第一个"

   
    k = strchr(q, '"');
    strncpy(file_md5, q, k-q);  //拷贝文件名
    file_md5[k-q] = '\0';
    
    
    p+=2;   
    begin = p;
    p = strstr(begin, "\r\n");
    p += 2;//\r\n
    len -= (p-begin);
  
    begin = p;  
    
       
    p = strstr(begin, "\r\n");
    p+=2;
    
    len -= (p-begin);  
    
    begin = p;
   
    f = strstr(begin, "name=");
    f += strlen("name=");
    f++;
    e = strchr(f, '"');
    
   
    strncpy(newfile, f, e - f);
    
#if 0
    pthread_mutex_lock(&pthread_mutex); 
    sprintf(errlog, "debug info: newfile =%s\r\n", newfile);                   
    fp = FCGI_fopen(upload_error,"ab+" );
    FCGI_fwrite(errlog, 1, strlen(errlog), fp);
    FCGI_fclose(fp);
    memset(errlog, 0x00, strlen(errlog));       
    pthread_mutex_unlock(&pthread_mutex);   
#endif 
    
    p = strstr(begin, "\r\n");      
    p+=2;  
    
    len -= (p-begin);
    
    begin = p;
    p = strstr(begin, "\r\n");
    p+=4;
    len -= (p-begin);
     
    //下面才是文件的真正内容  
    begin = p;  
    len -= (p - begin); 
 
    p = memstr(begin, len, boundary);
   
    if (p == NULL)
    {      
        ret = -1;        
        goto END;
    }
    else
    {
        p = p - 2;//\r\n
    }
    
     sprintf(fullpath, "%s/%s", filepath, file_md5);
         
     if(!opendir(filepath))
     {
        pthread_mutex_lock(&pthread_mutex);        
        sprintf(errlog, "error:upload file dir is NULL!\r\n");                  
        fp = FCGI_fopen(upload_error, "ab+");
        FCGI_fwrite(errlog, 1, strlen(errlog), fp);
        FCGI_fclose(fp);
        memset(errlog, 0x00, strlen(errlog));       
        pthread_mutex_unlock(&pthread_mutex);   
        ret = -1;
        goto END;       
     }
     
     
     #if 0
     /*
        判断文件是否存在     
     */
     while(access(fullpath, F_OK) != -1)
     {
        end_name = strrchr(file_md5, '.');
        strncpy(head_name, file_md5, end_name - file_md5);
        sprintf(file_md5, "%s(%d)%s", head_name, num, end_name);
        sprintf(fullpath, "%s/%s", filepath, file_md5);
        num++;      
     };
     #endif 
              
      fd = FCGI_fopen(fullpath, "wb+");
      if (fd < 0)
      {
          ret = -1;
          goto END;
      }
          
      FCGI_fwrite(begin, 1, (p-begin), fd);
      FCGI_fclose(fd);
    
END:
    if(file_buf)
    {
        free(file_buf);
        file_buf = NULL;
    }       
    FCGX_FFlush(request.in);   
    memset(fullpath, 0x00, strlen(fullpath));
    
    if(begin)
    {       
        begin = NULL;
    }   
    
    if(p)
    {   
        p = NULL;
    }
        
    if(q)
    {
        q = NULL;
    }
    
    if(k)
    {
        k = NULL;
    }   
        
    if(heto)
    {
        heto = NULL;
    }
        
    if(to)
    {
        to = NULL;
    }
        
    if(toen)
    {
        toen = NULL;
    }   
    
     if(filepath_head)
    {
        filepath_head = NULL;
    }        

    if(filepath_end)
    {
        filepath_end = NULL;
    }   
    if(end_name)
    {
      end_name = NULL;      
        
    }
      
    memset(head_name, 0x00, strlen(head_name));
    memset(content_text, 0x00, TEMP_BUF_MAX_LEN);
    memset(boundary, 0x00, TEMP_BUF_MAX_LEN);
    memset(file_md5, 0x00, sizeof(file_md5));
    return ret;
}





/*
    线程处理函数
*/
static void *doit(void *a)
{
    int rc, i, thread_id;
    thread_id = (int)((long)a);
    pid_t pid = gettid();
    char *server_name; 
    
    
#if 1
    pthread_mutex_lock(&pthread_mutex); 
    char errlog[256];
    MUX_THREAD_TEST++;
    sprintf(errlog, "-- pthread %d start MUX_THREAD_TEST= %d --\r\n", pid, MUX_THREAD_TEST);
    FCGI_FILE *fp = NULL;                   
    fp = FCGI_fopen(upload_error,"ab+" );
    FCGI_fwrite(errlog, 1, strlen(errlog), fp);
    FCGI_fclose(fp);
    memset(errlog, 0x00, strlen(errlog));       
    pthread_mutex_unlock(&pthread_mutex);     
#endif



    int count = 0;
    int flag = 0;
    
    long len = 0.0;
    int ret = 0;    
    
    char user[56] = {0};      //文件上传者
    long size = 0;
        
    char get_filePath[20] = "filepath";
    char get_fileName[20] = "filename";
    char get_fileMd5Value[56] = "fileMd5Value";
    char get_chunks[56] = "chunks";
    
    char *filepath = NULL;
        
    FCGX_Request request;   
        
    FCGX_InitRequest(&request, 0, 0); //初始化request
        
    for (;;)
    {       
        static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER;
        static pthread_mutex_t counts_mutex = PTHREAD_MUTEX_INITIALIZER;

        /* Some platforms require accept() serialization, some don't.. */        
        pthread_mutex_lock(&accept_mutex);
        rc = FCGX_Accept_r(&request); 
        pthread_mutex_unlock(&accept_mutex);
        
        if (rc < 0)
        {
            pthread_mutex_lock(&pthread_mutex); 
            sprintf(errlog, "pthread tid = %d, FCGX_Accept_r error!\r\n", pid);                 
            fp = FCGI_fopen(upload_error,"ab+" );
            FCGI_fwrite(errlog, 1, strlen(errlog), fp);
            FCGI_fclose(fp);
            memset(errlog, 0x00, strlen(errlog));       
            pthread_mutex_unlock(&pthread_mutex);   
            break;  
        }
                        
        server_name = FCGX_GetParam("SERVER_NAME", request.envp);                       //从环境变量中获取SERVER_NAME名                              
        FCGX_FPrintF(request.out,"Content-type: text/html; charset=UTF-8\r\n\r\n");                  
        char *contentLength = FCGX_GetParam("CONTENT_LENGTH", request.envp);            //获取环境变量    
        char *querystring = FCGX_GetParam("QUERY_STRING", request.envp);                            
        char name[256] = "method";
        
        char *method = get_query_string(querystring, name);                             
        char *filename = get_query_string(querystring, get_fileName);
        char *fileMd5Value = get_query_string(querystring, get_fileMd5Value);   
        char *chunks =  get_query_string(querystring, get_chunks);  
#if 0               
        pthread_mutex_lock(&pthread_mutex); 
        sprintf(errlog, "doit MUX_THREAD_TEST= %d\r\n", MUX_THREAD_TEST);                        
        fp = FCGI_fopen(upload_error,"ab+");
        FCGI_fwrite(errlog, 1, strlen(errlog), fp);
        FCGI_fclose(fp);
        memset(errlog, 0x00, strlen(errlog));       
        pthread_mutex_unlock(&pthread_mutex);           
    
        if(NULL == method)
        {
            FCGX_FPrintF(request.out,"{\"result\": false, \"msg\":method is NULL!}");
            FCGX_FFlush(request.out);       
            pthread_mutex_lock(&pthread_mutex);         
            sprintf(errlog, "method is NULL error tid = %d MUX_THREAD_TEST= %d\r\n", pid, MUX_THREAD_TEST);
            fp = FCGI_fopen(upload_error, "ab+");                                 
            FCGI_fwrite(errlog, 1, strlen(errlog), fp);
            FCGI_fclose(fp);            
            memset(errlog, 0x00, strlen(errlog));   
            pthread_mutex_unlock(&pthread_mutex);   
            goto END;   
        }
#endif  
                                                                                    
        if(strcasecmp(method, "check") == 0)    //判断文件是否存在
        {   
            char filepath[256] = "../file";                                                                                                                         
            if(!folderIsExit(filepath, fileMd5Value, request))
            {
                pthread_mutex_lock(&pthread_mutex); 
                sprintf(errlog, "%s dir ok!\r\n", fileMd5Value);                        
                fp = FCGI_fopen(upload_error,"ab+");
                FCGI_fwrite(errlog, 1, strlen(errlog), fp);
                FCGI_fclose(fp);
                memset(errlog, 0x00, strlen(errlog));       
                pthread_mutex_unlock(&pthread_mutex);                  
                FCGX_Finish_r(&request);                                
            }
            else
            {
                FCGX_FPrintF(request.out, "crean %s dir error!", fileMd5Value); 
                FCGX_Finish_r(&request);  
                    
            }
        }               
        else if(strcasecmp(method, "merge") == 0)
        {       
             char filepath[600] ={0};   
             int int_chunks  = atoi(chunks);    
             sprintf(filepath, "../file/%s", fileMd5Value);                      
             if(merge(filepath , int_chunks, filepath, filename, request) < 0)
             {
                 FCGX_FPrintF(request.out, "merge function error!");
                 FCGX_FFlush(request.out);
                 FCGX_Finish_r(&request);   
             }  
             else
             {
                 FCGX_FPrintF(request.out, "merge function ok!");
                 FCGX_FFlush(request.out);
                 FCGX_Finish_r(&request);   
             }                  
        }                   
        else if(strcasecmp(method, "upload") == 0)
        {   
            char new_file_name[256] = {0};
            char new_filemd5_value[256] = {0};
            sprintf(new_file_name, "%s", filename);
            sprintf(new_filemd5_value, "%s", fileMd5Value);
                                            
            if (contentLength != NULL)
            {               
                time_t timer;                               //time_t就是long int 类型
                struct tm *tblock;
                timer = time(NULL);
                tblock = localtime(&timer);

                len = strtol(contentLength, NULL, 10);      //字符串转long, 或者atol
                                    
                cJSON *fileinfo = cJSON_CreateObject();
                cJSON_AddStringToObject(fileinfo, "fileName", new_file_name);
                cJSON_AddStringToObject(fileinfo, "fileMd5Value", new_filemd5_value);
                cJSON_AddNumberToObject(fileinfo, "len", len);
                cJSON_AddStringToObject(fileinfo, "time", asctime(tblock));
                FCGX_FPrintF(request.out,"%s", cJSON_Print(fileinfo));  
                cJSON_Delete(fileinfo);                 
                FCGX_FFlush(request.out);                                                                                                                                             
            }
            else
            {
                len = 0;
            }   
                                            
            if (len <= 0)
            {
                FCGX_FPrintF(request.out, "No data from standard input\n");
                FCGX_FFlush(request.out);           
                ret = -1;
            }
            else
            {       
                char filepath[600] ={0};    
                sprintf(filepath, "../file/%s", fileMd5Value);                                                                                  
                if (recv_save_file(len, user, filename, &size, request, filepath) < 0)
                {   
                    
                    pthread_mutex_lock(&pthread_mutex); 
                    sprintf(errlog, "%s write error!\r\n", new_file_name);                        
                    fp = FCGI_fopen(upload_error, "ab+");
                    FCGI_fwrite(errlog, 1, strlen(errlog), fp);
                    FCGI_fclose(fp);
                    memset(errlog, 0x00, strlen(errlog));       
                    pthread_mutex_unlock(&pthread_mutex);                   
                    ret = -1;
                    goto END;
                }   
                else
                {
                    pthread_mutex_lock(&pthread_mutex); 
                    sprintf(errlog, "%s write ok!\r\n", new_file_name);                        
                    fp = FCGI_fopen(upload_error, "ab+");
                    FCGI_fwrite(errlog, 1, strlen(errlog), fp);
                    FCGI_fclose(fp);
                    memset(errlog, 0x00, strlen(errlog));       
                    pthread_mutex_unlock(&pthread_mutex);       
                }                                                                               
            }   
            FCGX_Finish_r(&request);            
        } 
        else if(strcasecmp(method, "wxupload") == 0)
        {   

        #if 0
            pthread_mutex_lock(&pthread_mutex);         
            char log[25] = {0};
            MUX_THREAD_TEST++;
            sprintf(log, "%d pid = %d", MUX_THREAD_TEST, pid);
            write_log(upload_error, log, strlen(log));
            memset(log, 0x00, strlen(log));
            pthread_mutex_unlock(&pthread_mutex);               
        #endif  
                
            if (contentLength != NULL)
            {                           
                filepath = NULL;                                        
                len = strtol(contentLength, NULL, 10);
                 
            #if 0           
                pthread_mutex_lock(&pthread_mutex);         
                sprintf(errlog, "contentLength  pthread tid = %d MUX_THREAD_TEST= %d !\r\n", pid, MUX_THREAD_TEST);
                fp = FCGI_fopen(upload_error, "ab+");                                 
                FCGI_fwrite(errlog, 1, strlen(errlog), fp);
                FCGI_fclose(fp);            
                memset(errlog, 0x00, strlen(errlog));   
                pthread_mutex_unlock(&pthread_mutex);
            #endif  
                
                if(wx_recv_save_file(len, user, filepath, filename, &size, request) < 0)
                {
                   FCGX_FPrintF(request.out, "{\"result\": false, \"msg\": file read data error, see upload_error.log!}");
                   FCGX_FFlush(request.out);                                                                
                   FCGX_Finish_r(&request);
                   continue;
                }
                else
                {
                    FCGX_FPrintF(request.out, "{\"result\": true, \"msg\":file upload ok!}");
                    FCGX_FFlush(request.out);   
                    MUX_THREAD_TEST++;                                                                                            
                }                                                                   
            }
            else
            {
                FCGX_FPrintF(request.out, "contentLength is NULL tid=%d!}", pid);
                goto END;
            }
        END:            
            FCGX_Finish_r(&request);
                                                                        
        }
        else 
        {
            FCGX_FPrintF(request.out,"{\"result\": false, \"msg\":tid = %d input function name error!}", pid);
            FCGX_FFlush(request.out);   
            FCGX_Finish_r(&request);  
        }                                        
    }                           
    return NULL; 
}
 



int main()
{
    long i;         
    pthread_t id[THREAD_COUNT];    
    FCGX_Init(); 
    
    for (i = 1; i < THREAD_COUNT; i++)  
    {
        pthread_create(&id[i], NULL, doit, (void*)i);  
        //pthread_detach(id[i]);  
    }
               
    doit(0);                                 
    return 0;   
}

编译脚本

#!/bin/bash
src="/home/UploadFile/src"
cgi="/home/UploadFile/cgi"
inc="/home/UploadFile/include"
obj="/home/UploadFile/obj"


rm ${obj}/qsi.o 
rm ${obj}/upload.o
rm ${obj}/cJSON_cgi.o

rm ${cgi}/upload.cgi

g++ -c -I ${inc} -DTBSVIDEODEAL_CGI -std=c++11 -o ${obj}/qsi.o ${src}/qsi.cpp
g++ -c -I ${inc} -DTBSVIDEODEAL_CGI -std=c++11 -o ${obj}/upload.o ${src}/upload.c
g++ -c -I ${inc} -DTBSVIDEODEAL_CGI -std=c++11 -o ${obj}/cJSON_cgi.o ${src}/cJSON.c

g++ -o ${cgi}/upload.cgi -g ${obj}/qsi.o ${obj}/upload.o ${obj}/cJSON_cgi.o -L /usr/local/lib -Wl,-Bstatic -l:libfcgi.a -Wl,-Bdynamic -lpthread -lmysqlclient

cp ${cgi}/upload.cgi ${cgi}/upload.fcgi
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • 第一章 Nginx简介 Nginx是什么 没有听过Nginx?那么一定听过它的“同行”Apache吧!Ngi...
    JokerW阅读 32,438评论 24 1,003
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,544评论 25 707
  • 写下这个篇名的时候,才想起这是成龙的一个电影名,也是经典哲学问题。 上周最热烈的事情莫过周三例会,这是谭欣第一次主...
    Amycym阅读 197评论 0 0
  • 张亚伟 人生的终极目标是什么? 这是一个值得思考的问题。不知道你的答案是什么?反正我一直被困扰了好多好多年。 很多...
    大伟传说阅读 474评论 0 1
  • 其实标题本来是想起“节日的意义”,但是逢中秋月夜,我们还是随景吧。 去年在知乎里回答过一个问题,中秋节于我的意义,...
    牙牙乐阅读 877评论 0 0