HTTP 的 multipart 类型

        上一篇文章讲到 http 的 MIME 类型 http MIME 类型 里有一个 multipart 多部分对象集合类型,这个类型 http 指南里有讲到:MIME 中的 multipart(多部分)电子邮件报文中包含多个报文,它们合在一起作为单一的复杂报文发送。每一部分都是独立的,有各自的描述及内容的集;不同的部分之间用分界字符串连接在一起。HTTP 也支持多部分主体,不过,通常只用在下列两种情形之一:提交填写好的表格,或是作为承载若干文档片段的范围响应。

        前端技术不懂,这里只用 postman 用为客户端来做示例。表格和文档形式,是不是像这样的呢?

当你选中其中一种情形时,http 的 Headers 里就会多出一个 Content-Type 表明这是一个多部分集合的类型报文:

表格情形还没试验过,这里主要讲文档情形的。所以呢,用客户端 postman 可以向服务端发送大的或是小的文档。下面给出服务端的例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string>
#include <map>
#include <mutex>
#include <sstream>
#include <iostream>
#include "mongoose.h"
#include "../logFormatPrt/log.h"#define FILE_NAME_LEN 128
#define FILE_PATH_LEN 32void eventHandler(struct mg_connection *nc, int event, void *eventData);
void fileUpload(mg_connection* nc, const int ev, void* data);
bool validPath(const char *path);struct userData
{int index;
};struct FileInfo
{FILE *fp; //打开新文件的指针char fileName[FILE_NAME_LEN]; //文件名,包含路径char filePath[FILE_PATH_LEN]; //文件路径size_t size; //文件大小,暂时没有用到size_t byteWrite;//已经写的字节数
};//用postman 测试,linux需要关闭防火墙,否则收不到数据
int main(int argc, char *argv[])
{   struct mg_mgr mgr;mg_mgr_init(&mgr, nullptr);int port = 8190;char buf[5] = {0};snprintf(buf, sizeof(buf), "%d", port);struct mg_connection *con = mg_bind(&mgr, buf, nullptr);if(con == NULL) {errorf("mg_bind fail\n");return -1;}mg_set_protocol_http_websocket(con);infof("listen ip[%s], port[%d]....\n", inet_ntoa(con->sa.sin.sin_addr), port); //uri是/fileUpload 时调用函数fileUploadmg_register_http_endpoint(con, "/fileUpload", fileUpload);while (1){mg_mgr_poll(&mgr, 100);}mg_mgr_free(&mgr);return 0;
}//触发的事件依次为:
//#define MG_EV_HTTP_MULTIPART_REQUEST 121 /* struct http_message */
//#define MG_EV_HTTP_PART_BEGIN 122        /* struct mg_http_multipart_part */
//#define MG_EV_HTTP_PART_DATA 123         /* struct mg_http_multipart_part */
//#define MG_EV_HTTP_PART_END 124          /* struct mg_http_multipart_part */
/* struct mg_http_multipart_part */
//#define MG_EV_HTTP_MULTIPART_REQUEST_END 125void fileUpload(mg_connection* nc, const int ev, void* data)
{//用户指针,用于保存文件大小,文件名struct FileInfo *userData = nullptr;//当事件ev是 MG_EV_HTTP_MULTIPART_REQUEST 时,data类型是http_messagestruct http_message *httpMsg = nullptr;if(MG_EV_HTTP_MULTIPART_REQUEST == ev){httpMsg = (struct http_message*)data;//初次请求时,申请内存if(userData == nullptr){userData = (struct FileInfo *)malloc(sizeof(struct FileInfo));memset(userData, 0, sizeof(struct FileInfo));}}else // 已经不是第一次请求了,nc->user_data 先前已经指向 userData,所以可以用了{userData = (struct FileInfo *)nc->user_data;}//当事件ev是 MG_EV_HTTP_PART_BEGIN/MG_EV_HTTP_PART_DATA/MG_EV_HTTP_PART_END 时,data类型是mg_http_multipart_partstruct mg_http_multipart_part *httpMulMsg = nullptr;if(ev >= MG_EV_HTTP_PART_BEGIN && ev <= MG_EV_HTTP_PART_END){httpMulMsg = (struct mg_http_multipart_part*)data;}switch(ev) {case MG_EV_HTTP_MULTIPART_REQUEST:{   ///query_string 为请求地址中的变量, key 名称约定好char filePath[32] = {0};std::string key("filePath");//从请求地址里获取 key 对应的值,所以这个需要和请求地址里的 key 一样//这里从地址中获取文件要上传到哪个路径if(mg_get_http_var(&httpMsg->query_string, key.c_str(), filePath, sizeof(filePath)) > 0) {tracef("upload file request, locate: %s = %s\n", key.c_str(), filePath); }if(!validPath(filePath)){tracef("no such directory of %s\n", filePath);std::string header;std::string body("no suce directory");header.append("HTTP/1.1 500 file fail").append("\r\n");header.append("Connection: close").append("\r\n");header.append("Content-Length: ").append(std::to_string(body.length())).append("\r\n").append("\r\n");header.append(body).append("\r\n");mg_send(nc, header.c_str(), header.length());nc->flags |= MG_F_SEND_AND_CLOSE;             }//保存路径,且 nc->user_data 指向该内存,下次请求就可以直接用了if(userData != nullptr){snprintf(userData->filePath, sizeof(userData->filePath), "%s", filePath);nc->user_data = (void *)userData;                 }}break;case MG_EV_HTTP_PART_BEGIN:  ///这一步获取文件名tracef("upload file begin!\n");if(httpMulMsg->file_name != NULL && strlen(httpMulMsg->file_name) > 0){tracef("input fileName = %s\n", httpMulMsg->file_name);//保存文件名,且新建一个文件,支持目录带 "/" 及不带 "/"if(userData != nullptr){if(userData->filePath[strlen(userData->filePath)] == '/'){snprintf(userData->fileName, sizeof(userData->fileName), "%s%s", userData->filePath, httpMulMsg->file_name);}else{snprintf(userData->fileName, sizeof(userData->fileName), "%s/%s", userData->filePath, httpMulMsg->file_name);}userData->fp = fopen(userData->fileName, "wb+");//创建文件失败,回复,释放内存if(userData->fp == NULL) {mg_printf(nc, "%s", "HTTP/1.1 500 file fail\r\n""Content-Length: 25\r\n""Connection: close\r\n\r\n""Failed to open a file\r\n");nc->flags |= MG_F_SEND_AND_CLOSE;free(userData);nc->user_data = nullptr;     return;}                    }}break;case MG_EV_HTTP_PART_DATA: //这一步写文件//tracef("upload file chunk size = %lu\n", httpMulMsg->data.len);if(userData != nullptr && userData->fp != NULL) {size_t ret = fwrite(httpMulMsg->data.p, 1, httpMulMsg->data.len, userData->fp);if(ret != httpMulMsg->data.len){mg_printf(nc, "%s","HTTP/1.1 500 write fail\r\n""Content-Length: 29\r\n\r\n""Failed to write to a file\r\n");nc->flags |= MG_F_SEND_AND_CLOSE;return;}userData->byteWrite += ret;  }break;case MG_EV_HTTP_PART_END:tracef("file transfer end!\n");if(userData != NULL && userData->fp != NULL){mg_printf(nc,"HTTP/1.1 200 OK\r\n""Content-Type: text/plain\r\n""Connection: close\r\n\r\n""Written %lu bytes of POST data to a file\n\n",userData->byteWrite);//设置标志,发送完成数据(如果有)并且关闭连接nc->flags |= MG_F_SEND_AND_CLOSE;//关闭文件,释放内存fclose(userData->fp);tracef("upload file end, free userData(%p)\n", userData);free(userData);nc->user_data = NULL;       } else{mg_printf(nc,"HTTP/1.1 200 OK\r\n""Content-Type: text/plain\r\n""Connection: close\r\n\r\n""Written 0 of POST data to a file\n\n");                }       break;case MG_EV_HTTP_MULTIPART_REQUEST_END:tracef("http multipart request end!\n");break;default:break;}
}bool validPath(const char *path)
{struct stat st;if(lstat(path, &st) == 0){return true;}return false;
}

如果想要直接编译则需要把头文件 #include "../logFormatPrt/log.h" 去掉,编译选项还得加上 -lssl -lcrypto,Makefile 如下:

#中间文件存放目录,如.o 和 .d 文件
COMPILE_DIR = compile
BIN_DIR = bin# 可编译arm版本
#CROSS = arm-himix200-linux-
CC = gcc -m32
CPP = $(CROSS)g++ -std=c++11 -m32
CFLAGS = -Werror -gLIB = -lpthread -lssl -lcrypto
#CPP_SRCS = $(wildcard *.cpp)
CPP_SRCS = $(shell ls -t | grep "\.cpp$$" | head -1)
CPP_OBJS = $(patsubst %.cpp, $(COMPILE_DIR)/%.o, $(CPP_SRCS))
CPP_DEP = $(patsubst %.cpp, $(COMPILE_DIR)/%.cpp.d, $(CPP_SRCS))C_SRCS = mongoose.c
C_OBJS = $(patsubst %.c, $(COMPILE_DIR)/%.o, $(C_SRCS))
C_DEP = $(patsubst %.c, $(COMPILE_DIR)/%.c.d, $(C_SRCS))OBJS = $(CPP_OBJS) $(C_OBJS)
DEP_ALL = $(CPP_DEP) $(C_DEP)$(shell if [ ! -d $(COMPILE_DIR) ]; then mkdir $(COMPILE_DIR); fi)
$(shell if [ ! -d $(BIN_DIR) ]; then mkdir $(BIN_DIR); fi)BIN =
ifeq ($(target), ) #如果是空的
BIN = a.out
else
BIN := $(target)
endifTARGET=$(BIN_DIR)/$(BIN)all: $(TARGET)-include $(DEP_ALL)$(TARGET): $(OBJS)$(CPP) $(CFLAGS) $^ -o $@ $(LIB)$(COMPILE_DIR)/%.o: %.cpp $(COMPILE_DIR)/%.cpp.d$(CPP) $(CFLAGS) -c $< -o $@$(COMPILE_DIR)/%.cpp.d: %.cpp$(CPP) $(CFLAGS) -MM -E -c $< -o $@@sed 's/.*\.o/$(subst /,\/,$(dir $@))&/g' $@ > $@.tmp@mv $@.tmp $@$(COMPILE_DIR)/%.o: %.c $(COMPILE_DIR)/%.c.d$(CC) $(CFLAGS) -c $< -o $@$(COMPILE_DIR)/%.c.d: %.c$(CC) $(CFLAGS) -MM -E -c $< -o $@@sed 's/.*\.o/$(subst /,\/,$(dir $@))&/g' $@ > $@.tmp@mv $@.tmp $@.PHONY: clean
clean:rm -rf $(COMPILE_DIR) $(BIN_DIR)

大到一个G的文件,上传也是没有问题的,1.3G 的文件:

如果问我客户端怎么写,这个我是不会的,但 postman 里有个“代码片段”选项,可以翻译成不同的编码语言的代码:

而在 Mongoose.c 源码里,是这样处理 multipart 类型的报文的,判断头部字段 Content-Type 为 multipart 时,交由相关处理函数进行处理,然后就 return 了,不再后续处理了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/265218.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Rocky Linux 运维工具 systemctl

一、​​systemctl​的简介 ​​systemctl​是用于管理系统服务的命令行工具。​systemctl​命令可以启动、停止、重启或重新加载服务&#xff0c;并管理它们。 二、systemctl​的参数说明 序号参数描述1start启动指定系统服务2stop停止指定系统服务3status显示指定系统服务的…

STM32 +合宙1.54“ 电子墨水屏(e-paper)驱动显示示例

STM32 合宙1.54“ 电子墨水屏&#xff08;e-paper&#xff09;驱动显示示例 &#x1f4cd;相关篇《Arduino框架下ESP32/ESP8266合宙1.54“ 电子墨水屏&#xff08;e-paper&#xff09;驱动显示示例》&#x1f516;程序是从GooDisplay品牌和微雪电子下同型号规格墨水屏的示例程序…

Ubuntu Mysql Innodb cluster集群搭建+MaxScale负载均衡(读写分离)

Ubuntu系统版本 20.04.3 LTS (Focal Fossa) 、64位系统。 cat /etc/os-release查看Ubuntu系统是32位还是64位 uname -m如果显示“i686”,则表示安装了32位操作系统。如果显示“x86_64”,则表示安装了64位操作系统。 一、安装MySql 参考: https://blog.csdn.net/qq_3712…

高频面试题整理(一)

文章目录 平台无关性如何实现&#xff1f;JVM如何加载 .class文件&#xff1f;什么是反射?谈谈ClassLoader谈谈类的双亲委派机制类的加载方式Java的内存模型?JVM内存模型-jdk8程序计数器&#xff1a;Java虚拟机栈局部变量表和操作数栈&#xff1a; Java内存模型中堆和栈的区别…

Linux之前后端项目部署与发布

目录 前言 一. Nginx配置安装&#xff08;自启动&#xff09; 1.1 一键安装4个依赖 1.2 上传并解压安装包 1.3 安装nginx 1.4 启动Nginx服务 1.5 防火墙规则 1.6 配置开机自启动 1.7 修改/etc/rc.d/rc/local的权限 二. Nginx负载多个tomcat 2.1 准备2个tomcat 2.2 修改第二个…

【Java设计模式】四、适配器模式

文章目录 1、适配器模式2、举例 1、适配器模式 适配器模式Adapter Pattern&#xff0c;是做为两个不兼容的接口之间的桥梁目的是将一个类的接口转换成客户希望的另外一个接口适配器模式可以使得原本由于接口不兼容而不能一起工作的那些类可以一起工作 最后&#xff0c;适配器…

阿里云中小企业扶持权益,助力企业开启智能时代创业新范式

在数字化浪潮的推动下&#xff0c;中小企业正面临着转型升级的重要关口。阿里云深知中小企业的挑战与机遇&#xff0c;特别推出了一系列中小企业扶持权益&#xff0c;旨在帮助企业以更低的成本、更高的效率拥抱云计算&#xff0c;开启智能时代创业的新范式。 一、企业上云权益…

自动驾驶消息传输机制-LCM

需要用到LCM消息通讯&#xff0c;遂研究下。 这里写目录标题 1 LCM简介2. LCM源码分析3 LCM C教程与实例3.1 安装配置及介绍3.2 创建类型定义3.3 初始化LCM3.4 发布publish一个消息3.5 订阅和接收一个消息3.6 LCM进程间通讯3.7 注意事项&#xff1f;3.7.1 当数据结构定义的是数…

SpringMVC 学习(十)之异常处理

目录 1 异常处理介绍 2 通过 SimpleMappingExceptionResolver 实现 3 通过接口 HandlerExceptionResolver 实现 4 通过 ExceptionHandler 注解实现&#xff08;推荐&#xff09; 1 异常处理介绍 在 SpringMVC中&#xff0c;异常处理器&#xff08;Exceptio…

go test用法(获取单元测试覆盖率)

go test用法&#xff08;获取ut覆盖率&#xff09; 为了提升系统的稳定性&#xff0c;一般公司都会对代码的单元测试覆盖率有一定要求。下面针对golang自带的测试命令go test做讲解。 1 命令 1.1 go test ./… &#xff08;运行当前目录及所有子目录下的测试用例&#xff09; …

13.云原生之常用研发中间件部署

云原生专栏大纲 文章目录 mysql主从集群部署mysql高可用集群高可用互为主从架构互为主从架构如何实现主主复制中若是两台master上同时出现写操作可能会出现的问题该架构是否存在问题&#xff1f; heml部署mysql高可用集群 nacos集群部署官网文档部署nacoshelm部署nacos redis集…

yolov9 瑞芯微芯片rknn部署、地平线芯片Horizon部署、TensorRT部署

特别说明&#xff1a;参考官方开源的yolov9代码、瑞芯微官方文档、地平线的官方文档&#xff0c;如有侵权告知删&#xff0c;谢谢。 模型和完整仿真测试代码&#xff0c;放在github上参考链接 模型和代码。 之前写过yolov8检测、分割、关键点模型的部署的多篇博文&#xff0c;y…

Mysql 高可用解决方案

1.环境说明 操作系统&#xff1a;centos7.7 主服务器&#xff1a;node2(192.168.1.102) 从服务器&#xff1a;node3(192.168.1.103) keepalived中虚拟ip(VIP):192.168.1.100 2.准备事项 主库和从库数据库的版本一致把主库的数据同步给从库一份 3.主库配置 3.1 编辑MySQL配…

读《Shape-Guided: Shape-Guided Dual-Memory Learning for 3D Anomaly Detection》

Chu Y M, Chieh L, Hsieh T I, et al. Shape-Guided Dual-Memory Learning for 3D Anomaly Detection[J]. 2023.&#xff08;为毛paperwithcode上面曾经的榜一引用却只有1&#xff09; 摘要 专家学习 无监督 第一个专家&#xff1a;局部几何&#xff0c;距离建模 第二个专家&…

postman访问k8s api

第一种方式&#xff1a; kubectl -n kubesphere-system get sa kubesphere -oyaml apiVersion: v1 kind: ServiceAccount metadata:annotations:meta.helm.sh/release-name: ks-coremeta.helm.sh/release-namespace: kubesphere-systemcreationTimestamp: "2023-07-24T07…

[Flutter]设置应用包名、名称、版本号、最低支持版本、Icon、启动页以及环境判断、平台判断和打包

一、设置应用包名 在Flutter开发中&#xff0c;修改应用程序的包名&#xff08;也称作Application ID&#xff09;涉及几个步骤&#xff0c;因为包名是在项目的Android和iOS平台代码中分别配置的。请按照以下步骤操作&#xff1a; 1.Android Flutter工程中全局搜索替换包名 …

在CentOS上使用Docker搭建Halo博客并实现远程访问的详细指南

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;网络奇遇记、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. Docker部署Halo1.1 检查Docker版本1.2 在Docker中部署Halo 二. Linux安装Cpol…

水库安全监测方案(福建地区水库安全监测案例分享)

我司星创易联最近在福建省受到了一个水库安全监测系统项目的委托。该水库位于福建中部山区,作为该地区的重要防洪与供水工程,对下游数十万人的生活产生重大影响。但是因为水库附近地质情况复杂,水库大坝在多次洪水冲击下出现一定病害,亟须全面加强对水库大坝安全状况的监测,以确…

Ethernet/IP转Modbus TCP网关

产品功能 1 YC-EIP-TCP工业级EtherNet/IP 网关 2 Modbus TCP 转 EtherNet/IP 3支持ModBus主从站 4 即插即用 无需编程 轻松组态 ,即实现数据交互 5导轨安装 支持提供EDS文件 6 EtherNET/IP与ModBus互转数据透明传输可接入PLC组态 支持CodeSys/支持欧姆龙PLC 支持罗克韦尔(AB) 典…

国辰智企MES系统优化企业管理,让生产制造更高效

在制造业的舞台上&#xff0c;MES制造执行管理系统如同一位出色的导演&#xff0c;将生产过程中的各个场景巧妙地连接起来&#xff0c;演绎出一场场精彩的制造盛宴。让我们一同走进MES在制造业的具体应用场景&#xff0c;感受它带来的变革与创新。 在生产计划与调度的场景中&am…