使用libevent evhttp做服务器提供文件上传、下载、查询功能

文件上传

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;
}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值