改寫文件上傳支持斷點續(xù)傳-后端C代碼

后端代碼

#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 "qsi.h"
#include "cJSON.h"

#define UPLOAD_LOG_MODULE "cgi"
#define UPLOAD_LOG_PROC   "upload"

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

#define THREAD_COUNT 20  
static int counts[THREAD_COUNT];

/*
    功能:獲取已經上傳過后的文件列表,(文件名,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);
        
    //創(chuàng)建一個文件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);
            
            //創(chuàng)建一個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));
                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));
    return 0;   
}

/*
    功能:判斷文件夾是否存在,不存在創(chuàng)建一個文件夾
    
    參數:
        filepath   文件夾路徑
        filename   文件夾名稱
    
    返回值:成功返回0 失敗返回-1
*/
int folderIsExit(char *filepath, char *filename, FCGX_Request request)
{       
    char fullpath[256];
    memset(fullpath, 0x00, sizeof(fullpath));
    
    sprintf(fullpath, "%s/%s", filepath, filename);
    
    if(access(fullpath, W_OK) == 0)
    {       
        //目錄存在且有寫的權限    遍歷目錄下所有文件組織文件鏈表
        getChunkList(filepath, filename,request);               
    }
    else
    {
        //目錄不存在或沒有寫的權限  
         mkdir(fullpath, S_IRWXU|S_IRWXG|S_IRWXO); //0777
         cJSON *fileinfo = cJSON_CreateObject();
         cJSON_AddStringToObject(fileinfo, "fileName", filename);       
         FCGX_FPrintF(request.out,"%s", cJSON_Print(fileinfo));
    }   
    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};
    memset(log,0x00, 526);
    int ret = 0;
    char *file_buf = NULL;
    char *begin = NULL;
    char *p, *q, *k;
    
     int fd = 0;
    int ret2 = 0;
    
    char *heto= NULL;
    char *to = NULL;
         
    int totallen = 0;
    char * toen = NULL;
    
    int num = 0;
     
    int FragmentName = 0;   
    char FragmentNum[50];
    
    char FPATH[256] = {0};
    
    char content_text[TEMP_BUF_MAX_LEN] = {0};      //文件頭部信息
    char boundary[TEMP_BUF_MAX_LEN] = {0};          //分界線信息

    //==========> 開辟存放文件的 內存 <===========
    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);         
      }
            
    //===========> 開始處理前端發(fā)送過來的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;
    //已經處理了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;
    len -= (p-begin);

    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(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);
    FCGX_FFlush(request.in);  //刷新標準輸入緩沖區(qū)
    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);
    }   
        
    FILE *pfw = fopen(fullpath, "wb");
    for (int i = 0; i < part_num; i++)
    {
        int length = getfilesize(newpath[i]);
        if (length != -1)
        {
            FILE *pfr = fopen(newpath[i], "rb"); //讀取
            for (int j = 0; j < length; j++)
            {
                char ch = fgetc(pfr);
                fputc(ch, pfw);                 //寫入
            }
            fclose(pfr);
            remove(newpath[i]);
        }
    }
    fclose(pfw);
    return 0;
}
    

/*
    線程處理函數
*/
static void *doit(void *a)
{
    int rc, i, thread_id;
    thread_id = (int)((long)a);
    pid_t pid = getpid();
    char *server_name;
    
    int count = 0;
    int flag = 0;
    
    long len;
    int ret = 0;    
    
    char filename[256] = {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";
    
    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)
            break;
        //從環(huán)境變量中獲取SERVER_NAME名      
        server_name = FCGX_GetParam("SERVER_NAME", request.envp);                                                     
        FCGX_FPrintF(request.out,"Content-type: text/html; charset=UTF-8\r\n\r\n");
        //獲取環(huá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(strcasecmp(method, "check") == 0)
        {   
            char filepath[256] = "../file";                                                                                                                 
            folderIsExit(filepath, fileMd5Value,request);
        }               
        else if(strcasecmp(method, "merge") == 0)
        {       
             char filepath[600] ={0};
             int int_chunks  = atoi(chunks);    
             sprintf(filepath, "../file/%s", fileMd5Value);  
             merge(filepath , int_chunks, filepath, filename, request);                     
        }                   
        else if(strcasecmp(method, "upload") == 0)
        {                                   
            if (contentLength != NULL)
            {
                len = strtol(contentLength, NULL, 10);  //字符串轉long, 或者atol
            }
            else
            {
                len = 0;
            }                       
            if (len <= 0)
            {
                FCGX_FPrintF(request.out, "No data from standard input\n");            
                ret = -1;
            }
            else
            {       
                char filepath[600] ={0};    
                sprintf(filepath, "../file/%s", fileMd5Value);                                                  
                if (recv_save_file(len, user, filename, &size, request, filepath) < 0)
                {                   
                    ret = -1;
                    return 0;
                }                                                                                   
            }               
        }                                         
        pthread_mutex_lock(&counts_mutex);
        ++counts[thread_id];
        pthread_mutex_unlock(&counts_mutex);    
        FCGX_Finish_r(&request);
    }
    
    return NULL;
}

int main()
{
    long i;  
    pthread_t id[THREAD_COUNT];    
    FCGX_Init();  
  
   //這里強制類型轉換需要注意一下我的系統是64位,32位可能會出錯
    for (i = 1; i < THREAD_COUNT; i++)          
        pthread_create(&id[i], NULL, doit, (void*)i);      
  
    doit(0);    
    return 0;   
}

這個是依賴于nginx、Fastcgi的程序,還需要CJson庫,供參考。
以上的解析post數據特定的,僅供參考。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容