后端代碼
#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數據特定的,僅供參考。