前言

笔者这里有需求需要用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实现多表单域文件上传相关推荐

  1. ASP.NET MVC (三、表单与文件上传)

    目录 前言: 1. 表单操作 2.文件上传 前言: 本章节主要针对文件上传进行强化练习,关键字[HttpPostedFileBase files,enctype="multipart/for ...

  2. php v9 上传_phpcms v9 表单添加文件上传字段

    phpcms v9 表单添加文件上传字段 1.打开目录 ./phpcms/modules/content/fields/ ;把 文件夹downfile,拷贝到目录./phpcms/modules/fo ...

  3. ajax 表单提交传文件,Ajax提交Form表单及文件上传

    刚刚申请下来的博客,写得第一篇.有点小激动,本人以前是一名工业3D设计师突然有些变故做上了JavaWeb开发: 前几天,发现了一些小问题.我在写后台管理页面时,需要上传一张图片.于是我就用很普通的Fo ...

  4. [RFC1867] HTML中基于表单的文件上传

    网络工作组:E. Nebel 征求意见:1867 L. Masinter 类别:试验 施乐公司 十一月 1995 HTML中基于表单的文件上传 这个备忘录的状态 这个备忘录为互联网社区定义了一个试验协 ...

  5. java form 上传文件_java通过表单进行文件上传的几种方法

    上传文件的分类: 无论什么方式上传文件,都要用post提交 方式一: 前端:表单方式上传文件 后端: 使用上传技术是apache中的Commons-fileupload.jar commons-io. ...

  6. php上传文件表单,php中关于普通表单多文件上传的处理方法

    然而有些情况只需要传递几个文件,而且文件体积并不太大,这种情况下使用组件则有点牛刀杀鸡的感觉,通过html自带的表单就可以实现需要的功能,关键在于后台接收程序的处理. php处理上传做的很方便,上传文 ...

  7. AJAX 提交表单以及文件上传

    本文转自:https://www.cnblogs.com/zhuxiaojie/p/4783939.html#autoid-0-0-0 作者:朱小杰 前言 使用ajax请求数据,很多人都会,比如说: ...

  8. 关于普通表单多文件上传的处理方法

    网页上传是Web开发时经常用到的功能,对于大量文件或大体积文件的情况可以考虑调用组件解决(如前文提到的SWFUpload组件).然而有些情况只需要传递几个文件,而且文件体积并不太大,这种情况下使用组件 ...

  9. 【一文学会文件上传】SpringBoot+form表单实现文件上传

    唠嗑部分 平时我们在项目过程中,往往会遇到这种情况,比如:我的用户应该有一个头像,那就涉及到文件上传,那么文件应该如何存储呢? 这就会有很多方式 1.最简单的就是存在服务器上,这就要考虑到服务器的磁盘 ...

最新文章

  1. FPGA基础之逻辑单元(LE or LC)的基本结构
  2. java 延迟初始化_Java - 延迟初始化
  3. mysql 回滚之后抛出异常_在PHP中 开始事务后,程序抛出异常 没有执行commit也没有执行rollback mysql事务会回滚吗?...
  4. css3切角文本框_CSS3:linear-gradient切角画册
  5. Realme真我X7系列首发骁龙860? 副总裁辟谣:大家散了吧
  6. Python_命名空间和作用域_25
  7. kafka_2.11-0.11.0.1集群搭建
  8. 任务 F :工时统计
  9. 慕测安居客功能测试答案
  10. 针对目前windows系统的所有勒索病毒补丁和安全工具
  11. Django基础教程
  12. C#,CAD二次开发,基于参考面参考点计算一个点的坐标
  13. 常用Java数据库连接池性能测试
  14. Please don't stop rua 233333
  15. 除了闹过腥风血雨的fastjson,你还知道哪些Java解析JSON的利器?
  16. MyBatis之通用mapper
  17. 使用P2P直播加速技术,IPTV直播系统可以节省多少带宽?
  18. h5学习笔记之canvas绘图(1)
  19. 利用 DTMaster 立体编辑 DEM 方法
  20. 驭势科技无人物流车亮相广州白云机场,推动民航智慧物流进入新时代

热门文章

  1. 适用于Photoshop的人像美容磨皮ps插件:Beauty Retouch Panel 2021 Mac
  2. PHP制作钱包银行金额数字展示功能实例
  3. qq浏览器打开word 技术原理_知道为什么计算机可以上网的同时,可以写word还可以听歌吗?...
  4. ant-design tree 设置默认选中状态_快速掌握文件夹位置的更改和文件的默认打开方式及重命名的操作...
  5. 【ES6(2015)】Map
  6. 基于java的络教学平台的设计与实现 (含源文件)
  7. Qt文档阅读笔记-隐式共享(Implicit Sharing)深入研究(理论及实例)
  8. Kafka笔记-搭建及单机生产者、消费者操作
  9. C++|Qt工作笔记-对explicit的认识(Qt中一般情况下为什么会自动加上这个关键字)
  10. Qt工作笔记-QML与C++交互