文件上传
post接口/xpoc/ue-fse-uploadfd请求报文如下:
请求头header如下
body如下:
服务器200 ok应答
文件下载
get接口 /xpoc/ue-fse-downloadfd/9501f4f82ea3f663d92097e5e66f6253
http get请求:
http get的应答
//上传post接口/xpoc/ue-fse-uploadfd请求报文如下:
请求头header如下
在这里插入图片描述
int HttpSipMsgUpFileAck(struct evhttp_request *req, char *keyFileName, char *aucFileName, char *aucSessionId)
{
cJSON *pRspBody = cJSON_CreateObject();
do
{
if (NULL == pRspBody)
{
printf("Can not creat RspBody\n");
break;
}
//构造应答json字符串
cJSON *pURLItem = cJSON_CreateString(keyFileName);
if (NULL == pURLItem)
{
printf("Can not creat numberjson for param! param:pURLItem[%s]\n", keyFileName);
break;
}
cJSON_AddItemToObject(pRspBody, "URL", pURLItem);
cJSON *pFileNameItem = cJSON_CreateString(aucFileName);
if (NULL == pFileNameItem)
{
printf("Can not creat numberjson for param! param:pFileNameItem[%s]\n", aucFileName);
break;
}
cJSON_AddItemToObject(pRspBody, "FileName", pFileNameItem);
cJSON *pFileSessionIdItem = cJSON_CreateString(aucSessionId);
if (NULL == pFileSessionIdItem)
{
printf("Can not creat numberjson for param! param:pFileSessionIdItem[%s]\n", aucSessionId);
break;
}
cJSON_AddItemToObject(pRspBody, "SessionId", pFileSessionIdItem);
//send response
struct evbuffer *retbuff = evbuffer_new();
if(retbuff == NULL)
{
printf("====line:%d,%s\n", __LINE__, "retbuff is null.");
return;
}
evhttp_add_header(req->output_headers, "Content-Type", "application/json;charset=UTF-8");
evhttp_add_header(req->output_headers, "Connection", "Keep-Alive");
evbuffer_add_printf(retbuff, cJSON_PrintUnformatted(pRspBody));
evhttp_send_reply(req, 200, "OK", retbuff);
evbuffer_free(retbuff);
cJSON_Delete(pRspBody);
return 0;
} while (0);
cJSON_Delete(pRspBody);
//reqFailAck417(req,"70405"); 这里直接回复其他自定义错误码
return -1;
}
//文件上传接口的回调处理函数
void HttpUpFile(struct evhttp_request *req, void *arg)
{
char * pBoundary = NULL;
char strtemp[260];
if(EVHTTP_REQ_POST == req->type)
{
pBoundary = get_boundary(evhttp_find_header(req->input_headers, "Content-Type"));
if(NULL != pBoundary)
{
int iBoundaryLen = strlen(pBoundary);
size_t post_size = evbuffer_get_length(req->input_buffer);//获取数据长度
if(post_size > 0)
{
char *search = (char*)(evbuffer_pullup(req->input_buffer, -1));
char *end = search + post_size;
char *pstart = strstr(search, "\r\n\r\n");
char *pend = NULL;
if(pstart != NULL && pstart < end)
{
pstart += 4;
memset(strtemp, 0, sizeof(strtemp));
sprintf(strtemp,"\r\n--%s\r\n", pBoundary);
pend = strstr(pstart, strtemp);
if(pend != NULL && pend < end)
{
//pend指针指向JSON字符串尾部 pstart指针指向JSON字符串头部
/*解析json 获取文件信息,是JSON格式如下:
{”UserID“:"XXXXXXXXX","FileName":"xxxxxxx","FileMd5":"xxxxxxxxx","FileSize":12345}
这里通过JSON获取上传文件名、大小及MD5信息等
后续添加代码
*/
pstart = strstr(pend + iBoundaryLen + 6, "\r\n\r\n");
if(pstart != NULL && pstart < end)
{
pstart += 4;
memset(strtemp, 0, sizeof(strtemp));
sprintf(strtemp,"\r\n--%s\r\n", pBoundary);
int iFile_size = post_size - (pstart - search) - strlen(strtemp); //上传内容的大小
/*这里获取上传文件大小就可以保存到服务器指定目录下
这里自行添加
*/
HttpSipMsgUpFileAck(req, keyFileName, fileName, aucSessionId);
}
else
{
printf("pstart == NULL or pstart >= end [2].");
}
}
else
{
printf("pend == NULL or pend >= end.");
}
}
else
{
printf("pstart == NULL or pstart >= end [1].");
}
}
else
{
printf("post_size[%d] <= 0.", post_size);
}
}
else
{
printf("pBoundary is NULL."); //在http post请求header中Content-Type字段中未发现pBoundary
}
}
if (NULL != pBoundary) free(pBoundary);
return;
}
/*文件下载接口回调处理函数
http get请求头如下:
GET /xpoc/ue-fse-downloadfd/9501f4f82ea3f663d92097e5e66f6253 HTTP/1.1
range: bytes=0-18057
Host: 119.45.227.92:8030
Connection: Keep-Alive
User-Agent: okhttp/3.12.0
\r\n
对于http get请求服务端的206应答
HTTP/1.1 206 Partial Content
Content-Type: application/octet-stream
Connection: Keep-alive
Content-Range: bytes 0-18057/18058
Content-Length: 18058
Date: Fri, 04 Mar 2022 03:21:14 GMT
\r\n
******** 这里是文件的内容,大小为18058字节
*/
typedef struct {
struct evhttp_request *req;
int fd;
long chunksize;
long long filesize;
long long offset;
int count;
}FseChunkReqStateS ;
void FseChunkedTrickleCb(struct evhttp_connection *conn, void *arg)
{
FseChunkReqStateS *state = (FseChunkReqStateS *)arg;
FseHttpSendChunk(state);
}
int FseHttpSendChunk(FseChunkReqStateS *crs)
{
lseek(crs->fd, crs->offset, SEEK_SET);
long length = crs->filesize - crs->offset;
if(length > crs->chunksize)
{
length = crs->chunksize;
}
char *Buffer = (char *)malloc(length + 1);
if(Buffer != NULL)
{
memset(Buffer, 0, length+1);
}
else
{
if(crs->fd != -1) close(crs->fd);
return -1;
}
int iread = read(crs->fd, Buffer, length);
if(iread > 0)
{
struct evbuffer *evb = evbuffer_new(); //向evb buffer里写回复的chunk数据
evbuffer_add(evb, Buffer, iread);
if (crs->offset < crs->filesize) //循环写chunk模式的数据,保证请求的字节数被覆盖
{
evhttp_send_reply_chunk_with_cb(crs->req, evb, FseChunkedTrickleCb, crs);
crs->offset += iread;
crs->count++;
}
else
{
evhttp_send_reply_end(crs->req); //这里是应答结束 send_reply_end 写数据完毕
if(crs->fd != -1) close(crs->fd);
free(crs);
crs = NULL;
}
evbuffer_free(evb);
}
else
{
evhttp_send_reply_end(crs->req);
if(crs->fd != -1) close(crs->fd);
free(crs);
crs = NULL;
}
free(Buffer);
return 0;
}
void sendHttpFileDownloadRsp(struct evhttp_request *req, char *fileName)
{
//这里是应答报文header头部各字段
int filelen = get_file_size(fileName); //该函数暂未实现,获取文件大小
char fileSize[16] = {0};
sprintf(fileSize, "%d", filelen);
char contRange[64] = {0};
//形如格式 0-10875/10876
sprintf(contRange, "bytes %d-%d/%d", rangbeg, rangend>0?rangend:filelen-1, filelen);
evhttp_add_header(req->output_headers, "Content-Type", "application/octet- stream");
evhttp_add_header(req->output_headers, "Connection", "Keep-alive");
evhttp_add_header(req->output_headers, "Content-Range", contRange);
evhttp_add_header(req->output_headers, "Content-Length", fileSize); //返回的文件大小
evhttp_send_reply_start(req, 206, "Partial Content"); //这里是应答开始 send_reply_start
FseChunkReqStateS *state = (FseChunkReqStateS *)malloc(sizeof(FseChunkReqStateS));
//初始化并赋值
FseHttpSendChunk(state);
}
void HttpSipMsgFileDownload(struct evhttp_request *req)
{
char strtemp[260];
int ulReturn = 0;
char *pEnd = req->uri + strlen(req->uri);
char *pUrl = strrchr(req->uri,'/');
if (pUrl != NULL && pUrl < pEnd)
{
pUrl = pUrl + 1;
const char *pRange = evhttp_find_header(req->input_headers, "Range");
if (pRange) //发现Range字段则为分片下载
{
//这里添加服务端的分片下载处理流程
}
else //全量下载
{
//这里添加服务端的全量下载处理流程
xxxxxxxxxxxxxxxxxxxxxxx
//http get下载的应答
sendHttpFileDownloadRsp(req, "xxxx文件名");
}
}
return;
}
//json解析文件查询请求
/*
格式如下:
{"userID":"XXXXXXXX",
"FileList":[{
"FileName":"XXXXXXX",
"FileSize":xxxxxx,
"FileDuration":"XXXXXXX",
"FileType":xxxxxxx
},....]
}
*/
int decodeOffLineFileInfo(char*pHttpBody)
{
cJSON *pReqBody = cJSON_Parse(pHttpBody);
if (NULL == pReqBody)
{
printf("http request body is not json body!\n");
return -1;
}
cJSON *ptJson = NULL;
ptJson = cJSON_GetObjectItem(pReqBody,"UserID");
if(ptJson && (ptJson->valuestring != NULL) && (strlen(ptJson->valuestring) != 0))
{
char userId[32 + 1] = {0x00};
strcpy(userId, ptJson->valuestring);
}
else
{
printf("http request body do not have [UserID] or [UserID] is NULL!\n");
cJSON_Delete(pReqBody);
return -1;
}
cJSON *pJSON_FileList = cJSON_GetObjectItem(pReqBody,"FileList");
if (NULL == pJSON_FileList)
{
printf("not find array param FileList!\n");
cJSON_Delete(pReqBody);
return -1;
}
ptProfile->ulNum = cJSON_GetArraySize(pJSON_FileList);
if (ptProfile->ulNum == 0 || ptProfile->ulNum > QRY_OFFLINEFILE_MAXNUM)
{
printf("FileList do not have file or have too more files, FileNum[%d] !\n", ptProfile->ulNum);
cJSON_Delete(pReqBody);
return -1;
}
cJSON *ptJsonFileMember;
UCHAR i = 0;
for (i = 0; i < ptProfile->ulNum; i++)
{
ptJsonFileMember = cJSON_GetArrayItem(pJSON_FileList, i);
ptJson = cJSON_GetObjectItem(ptJsonFileMember,"FileName");
if (ptJson != NULL && (ptJson->valuestring != NULL) && (strlen(ptJson->valuestring) != 0))
{
char fileName[64] = {0x00};
strcpy(fileName, ptJson->valuestring);
}
ptJson = cJSON_GetObjectItem(ptJsonFileMember,"FileSize");
if (ptJson != NULL && ptJson->valueint > 0)
{
int fileSize = ptJson->valueint;
}
ptJson = cJSON_GetObjectItem(ptJsonFileMember,"FileDuration");
if(ptJson != NULL && (ptJson->valuestring != NULL) && (strlen(ptJson->valuestring) != 0))
{
char fileDuration[64] = {0x00};
strcpy(fileDuration, ptJson->valuestring);
}
ptJson = JSON_GetObjectItem(ptJsonFileMember,"FileType");
if(ptJson)
{
int fileType = ptJson->valueint;
}
}
cJSON_Delete(pReqBody);
return 0;
}
//查询结果通过JSON格式回复,目前写的是一个流程(伪代码)
UINT HttpOffLineFileQryAck(struct evhttp_request *req, QryFileAckS *ptFileQryAck)
{
XPOC_TRACEFUNC_IN;
cJSON *pRspBody = cJSON_CreateObject();
do
{
if (NULL == pRspBody)
{
printf("Can not creat RspBody\n");
break;
}
cJSON *fileArray = cJSON_CreateArray();
UCHAR i = 0;
int num =xxx; //文件个数
for (i=0; i < num; i++)
{
cJSON *fileItem = cJSON_CreateObject();
cJSON *pFileNameItem = cJSON_CreateString((char *)ptFileQryAck->stFileUpState[i].aucFileName);
if (NULL == pFileNameItem)
{
break;
}
cJSON_AddItemToObject(fileItem, "FileName", pFileNameItem);
cJSON *pFileUpLoadedSizeItem = cJSON_CreateNumber(fileUpLoadedSize);
if (NULL == pFileUpLoadedSizeItem)
{
break;
}
cJSON_AddItemToObject(fileItem, "FileUpLoadedSize", pFileUpLoadedSizeItem);
cJSON_AddItemToArray(fileArray, fileItem);
}
cJSON_AddItemToObject(pRspBody, "FileList", fileArray);
//send response
struct evbuffer *retbuff = evbuffer_new();
if(retbuff == NULL)
{
printf("====line:%d,%s\n", __LINE__, "retbuff is null.");
return;
}
evhttp_add_header(req->output_headers, "Content-Type", "application/json;charset=UTF-8");
evhttp_add_header(req->output_headers, "Connection", "Keep-Alive");
evbuffer_add_printf(retbuff, cJSON_PrintUnformatted(pRspBody));
evhttp_send_reply(req, 200, "OK", retbuff);
evbuffer_free(retbuff);
cJSON_Delete(pRspBody);
return 0;
} while (0);
return -1;
}
//查询文件上传情况
void HttpOffLineFileQuery(struct evhttp_request *req, void *arg)
{
int ulReturn = 0;
if (EVHTTP_REQ_POST == req->type)
{
struct evbuffer* buf = evhttp_request_get_input_buffer(req);
size_t len = evbuffer_get_length(buf);
printf("len:%zu, body size:%zu", len, req->body_size);
char *tmp = malloc(len + 1);
memcpy(tmp, evbuffer_pullup(buf, -1), len);
tmp[len] = '\0';
printf("HTML BODY:%s\n", tmp);
decodeOffLineFileInfo(tmp); //解析post请求中json字符串
free(tmp);
return;
}
return;
}
/*
使用evhttp启动http server,并支持文件上传、下载、查询等功能
http_addr:ip地址
http_port:端口
*/
int evhttpServer_start(char *http_addr, int http_port)
{
struct evhttp *http_server = NULL;
//初始化
event_init();
//启动http服务端
http_server = evhttp_start(http_addr, http_port);
if(http_server == NULL)
{
printf("http server start failed.");
return -1;
}
//设置请求超时时间(s)
evhttp_set_timeout(http_server, HTTP_REQUEST_MAX_TIMELEN);
//设置事件处理函数,evhttp_set_cb针对每一个事件(请求)注册一个处理函数,
//区别于evhttp_set_gencb函数,是对所有请求设置一个统一的处理函数
evhttp_set_cb(http_server, "/xpoc/ue-fse-uploadfd", HttpUpFile, NULL);
//转get方式
//evhttp_set_cb(http_server, "/xpoc/ue-fse-downloadfd", HttpDownFile, NULL);
evhttp_set_gencb(http_server, HttpDownFile,NULL);
evhttp_set_cb(http_server, "/xpoc/ue-fse-offline-uploadfd", HttpOffLineUpFile, NULL);
evhttp_set_cb(http_server, "/xpoc/ue-fse-offline-query", HttpOffLineFileQuery, NULL);
//循环监听. //此处处理http_handler_msg
event_dispatch();
//实际上不会释放,代码不会运行到这一步
evhttp_free(http_server);
return 0;
}