C/C++:mongoose.c实现多表单域文件上传
生活随笔
收集整理的這篇文章主要介紹了
C/C++:mongoose.c实现多表单域文件上传
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
前言
筆者這里有需求需要用mongoose.c在上傳文件的時候同時還要提交其他表單字段,百度一圈居然沒有一個完整的例子,都是把源碼示例的上傳文件例子抄,講也沒講明白。源碼示例都是只有1個文件域上傳。由于mongoose.c 比較輕量級,許多api也是在不斷更新。為了避免大家浪費時間,先聲明筆者這里用的mongoose.c 的 6.13版本,版本在mongoose.h MG_VERSION宏中有聲明版本。這里筆者也把示例代碼拿來改造做1個多input項的文件上傳示例 做個拋磚引玉,當然也會給大家講得明明白白,有圖有真相。
講解
需求
這里筆者只處理2個input項,第1個input項 path 指定服務器 將上傳的文件存儲在指定路徑下,第2個為 input file。
<input name="path" type="text" value="d:\\1.txt"/> <input name="file" type="file" />多個文件input項以及多個普通input 道理都一樣,讀者可以看完 可以舉一反三的。
第1步 注冊處理文件上傳的http接口
/*上傳文件接口注冊參數1:mg_connection*參數2:uri,這里是/upload參數3:處理該請求的回調函數,當/upload這個uri請求到來,就會走handle_upload這個回調函數。參數4:可選參數,MG_UD_ARG是可以傳指定參數的 需要在mongoose.h中開啟 MG_ENABLE_CALLBACK_USERDATA 宏。最后一個參數有啥用呢?比如我們有多個不同業務需求(多個不同的uri)的表單form-data(一般只用在文件上傳中)形式傳輸數據的,可以將代碼抽象出來 所有上傳接口都用1套代碼,將可變的數據都放到最后1個參數指針中(比如 傳輸完畢后的回調函數、不同uri上傳文件存儲位置等)如果只是單純的業務數據直接ajax發post請求即可,此種mongoose server可以參考示例restful_server。筆者這里只是拋轉引玉,這里代碼就不展開了。*/ mg_register_http_endpoint(c, "/upload", handle_upload MG_UD_ARG(NULL));第2步 編寫處理文件上傳的回調函數
/*一次form-data表單數據請求,該函數會被回調多次。具體參數含義如下: 參數1:mg_connection* 參數2:這里的ev參數,有下面5種情況,決定此次回調函數的功能,具體情況如下。MG_EV_HTTP_MULTIPART_REQUEST // 表單請求開始,1次表單提交請求會有1次,可以獲取請求頭相關數據。MG_EV_HTTP_PART_BEGIN // 1個表單input項數據傳輸開始,可以取到input name的值,回調n次(多少個input項回調多少次) MG_EV_HTTP_PART_DATA // 1個表單input項數據傳輸中,可以取到input value的值,如果是文件則為文件流,回調n*m次 MG_EV_HTTP_PART_END // 1個表單input項數據結束,回調n次(多少個input項回調多少次) MG_EV_HTTP_MULTIPART_REQUEST_END // 表單請求結束(也就是整個表單數據傳輸完畢),也是1次。// 可以進行數據響應操作,當然如果只是一個input file項,也可在MG_EV_HTTP_PART_END做數據響應操作。 參數3:回調函數攜帶的數據,與參數2有關聯。 參數4:可以是mg_register_http_endpoint綁定的第4個參數,同時也是nc->user_data。這里筆者只處理2個input項 <input name="path" type="text" value="d:\\1.txt"/> <input name="file" type="file" /> */ static void handle_upload(struct mg_connection *nc, int ev, void *p MG_UD_ARG(user_data))代碼
需要注意的是,需要在mongoose.h中打開http form-data文件上傳功能,宏定義如下:
#define MG_ENABLE_HTTP_STREAMING_MULTIPART 1 // 開啟http form-data表單上傳文件核心代碼如下。完整工程可以點這里進行下載。
// 自定文件上傳item,這里不做復雜了 struct MyUploadItem{struct file_writer_data fileData;// 存放文件路徑char path[MAX_PATH];// 原始文件名char fileName[MAX_PATH]; }; /*一次form-data表單數據請求,該函數會被回調多次。具體參數含義如下: 參數1:mg_connection* 參數2:這里的ev參數,有下面5種情況,決定此次回調函數的功能,具體情況如下。MG_EV_HTTP_MULTIPART_REQUEST // 表單請求開始,1次表單提交請求會有1次,可以獲取請求頭相關數據。MG_EV_HTTP_PART_BEGIN // 1個表單input項數據傳輸開始,可以取到input name的值,回調n次(多少個input項回調多少次) MG_EV_HTTP_PART_DATA // 1個表單input項數據傳輸中,可以取到input value的值,如果是文件則為文件流,回調n*m次 MG_EV_HTTP_PART_END // 1個表單input項數據結束,回調n次(多少個input項回調多少次) MG_EV_HTTP_MULTIPART_REQUEST_END // 表單請求結束(也就是整個表單數據傳輸完畢),也是1次。// 可以進行數據響應操作,當然如果只是一個input file項,也可在MG_EV_HTTP_PART_END做數據響應操作。 參數3:回調函數攜帶的數據,與參數2有關聯。 參數4:可以是mg_register_http_endpoint綁定的第4個參數,同時也是nc->user_data。這里筆者只處理2個input項 <input name="path" type="text" value="d:\\1.txt"/> <input name="file" type="file" /> */ static void handle_upload(struct mg_connection *nc, int ev, void *p MG_UD_ARG(user_data)) {// nc攜帶的自定義數據struct MyUploadItem *upData = (struct MyUploadItem *) nc->user_data;// MG_EV_HTTP_MULTIPART_REQUEST 事件回調時,參數p為http_message*struct http_message* hm = (struct http_message*)p;// MG_EV_HTTP_PART_BEGIN、MG_EV_HTTP_PART_DATA、MG_EV_HTTP_PART_END 事件回調時,參數p為mg_http_multipart_part*struct mg_http_multipart_part *mp = (struct mg_http_multipart_part *) p;char pTemp[128] = {0};switch (ev) {case MG_EV_HTTP_MULTIPART_REQUEST: {struct mg_str* mgTemp;printf("======form-data Begin=======================\n");// 在該EV中 可獲取請求頭中的數據printf("======Request Head Data begin======\n");mgTemp = mg_get_http_header(hm, "Content-Length");memcpy(pTemp, mgTemp->p, mgTemp->len);printf("Content-Length:%s \n", pTemp);mgTemp = mg_get_http_header(hm, "Content-Type");memset(pTemp, 0, 128);memcpy((void*)pTemp, (void*)mgTemp->p, mgTemp->len);printf("Content-Type:%s \n", pTemp);printf("======Request Head Data end========\n");if(upData == NULL){upData = (struct MyUploadItem*)calloc(1, sizeof(struct MyUploadItem));// 這里這樣賦值后,在下次回調upload_handle函數時,第4個參數就用上了nc->user_data = (void *) upData;}break;}case MG_EV_HTTP_PART_BEGIN: {printf("======one input begin======\n");// 在該EV中 可以獲取input name 以及 filename,filename不為空時就為input fileif( mp->file_name != NULL && strlen(mp->file_name) > 0){// 該input項為 input file. 此時就可以打開文件了,在MG_EV_HTTP_PART_DATA中就可以直接寫數據了printf("input name:%s filename:%s\n", mp->var_name, mp->file_name);strcpy(upData->fileName, mp->file_name);upData->fileData.fp = fopen(upData->path, "wb+");;upData->fileData.bytes_written = 0;if (upData->fileData.fp == NULL) {mg_printf(nc, "%s","HTTP/1.1 500 Failed to open a file\r\n""Content-Length: 0\r\n\r\n");nc->flags |= MG_F_SEND_AND_CLOSE;return;}}else{// 普通input項 printf("input name:%s \n", mp->var_name);}break;}case MG_EV_HTTP_PART_DATA: {if( mp->file_name != NULL && strlen(mp->file_name) > 0){// input fileif (fwrite(mp->data.p, 1, mp->data.len, upData->fileData.fp) != mp->data.len) {mg_printf(nc, "%s","HTTP/1.1 500 Failed to write to a file\r\n""Content-Length: 0\r\n\r\n");nc->flags |= MG_F_SEND_AND_CLOSE;return;}upData->fileData.bytes_written += mp->data.len;printf("input name:%s filename:%s writeLen:%d \n", mp->var_name, mp->file_name, upData->fileData.bytes_written);}else if(strcmp(mp->var_name, "path") == 0){memcpy(pTemp, mp->data.p, mp->data.len );// 普通input項 printf("input name:%s value:%s \n", mp->var_name, pTemp);strcpy(upData->path, pTemp);}break;}case MG_EV_HTTP_PART_END: {printf("======one input end======\n");break;}case MG_EV_HTTP_MULTIPART_REQUEST_END: {printf("======form-data End=======================\n\n");mg_printf(nc,"HTTP/1.1 200 OK\r\n""Content-Type: text/plain\r\n""Connection: close\r\n\r\n""%s[len=%d] to %s success.\n\n",upData->fileName, (long) ftell(upData->fileData.fp), upData->path);nc->flags |= MG_F_SEND_AND_CLOSE;// 關閉文件fclose(upData->fileData.fp);// 釋放calloc分配的內存free(upData);nc->user_data = NULL;break;}} }測試結果
普通表單上傳測試
postman測試
mongoose 服務器打印日志
總結
以上是生活随笔為你收集整理的C/C++:mongoose.c实现多表单域文件上传的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Latex可能遇到的一些问题
- 下一篇: CentOS64位下python2.6升