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 的 multipart 类型
上一篇文章讲到 http 的 MIME 类型 http MIME 类型 里有一个 multipart 多部分对象集合类型,这个类型 http 指南里有讲到:MIME 中的 multipart(多部分)电子邮件报文中包含多个报文,它们合在一起作为单一的复杂报文发送…...
【NR 定位】3GPP NR Positioning 5G定位标准解读(二)
目录 前言 2. UE定位方法的作用 3. 标准UE定位方法 3.1 引言 3.2 网络辅助的GNSS方法 3.3 OTDOA定位 3.4 增强小区ID方法 3.5 气压传感器定位 3.6 WLAN定位 3.7 蓝牙定位 3.8 TBS定位 3.9 运动传感器定位 3.10 NR增强小区ID方法 3.11 多RTT定位 3.12 DL-AoD定位…...
为何代理IP的稳定性不如有线IP?
代理IP与网线IP的稳定性之间存在差异的原因在于它们的工作机制和服务环境的不同。以下是代理IP不如网线IP稳定的一些主要原因: 1. 服务提供商的质量: - 动态分配:代理IP通常是动态分配的,这意味着每次请求或每隔一段时间ÿ…...
【c++】 string类的模拟实现
1.浅拷贝 浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以…...
transformer--transformer模型构建和测试
前面几节进行了各种组件的学习和编码,本节将组件组成transformer,并对其进行测试 EncoderDecoder 编码器解码器构建 使用EnconderDecoder实现编码器-解码器结构 # 使用EncoderDeconder类实现编码器和解码器class EncoderDecoder(nn.Module):def __ini…...
从0到1全流程使用 segment-anything
从0到1全流程使用 segment-anything 一、安装 anaconda 一、下载 anaconda 二、以管理员身份运行安装 1、勾选 Just Me 2、统一安装路径(后续 python 等包也安装至此目录) 3、勾选 add to path 然后安装即可。 三、修改 Anaconda 默认路径及默认缓存路径 Anaconda 默认下…...
Window系统部署Splunk Enterprise并结合内网穿透实现远程访问本地服务
文章目录 前言1. 搭建Splunk Enterprise2. windows 安装 cpolar3. 创建Splunk Enterprise公网访问地址4. 远程访问Splunk Enterprise服务5. 固定远程地址 前言 本文主要介绍如何简单几步,结合cpolar内网穿透工具实现随时随地在任意浏览器,远程访问在本地…...
Windows服务器:通过nginx反向代理配置HTTPS、安装SSL证书
先看下效果: 原来的是 http,配置好后 https 也能用了,并且显示为安全链接。 首先需要 SSL证书 。 SSL 证书是跟域名绑定的,还有有效期。 windows 下双击可以查看相关信息。 下载的证书是分 Apache、IIS、Tomcat 和 Nginx 的。 我…...
LeetCode67 二进制求和
题目 给你两个二进制字符串 a 和 b ,以二进制字符串的形式返回它们的和。 示例 示例 1:输入:a "11", b "1" 输出:"100" 示例 2:输入:a "1010", b "1011" 输出&…...
python | 列表,元组,字符串,集合,字典
列表: 可以容纳任意数目不同类型的元素(支持我们学过的任意数据类型)元素之间有先后顺序用中括号包裹,每个元素用逗号隔开 例如: students [林黛玉,薛宝钗,贾元春,贾探春,史湘云,妙玉,贾迎春,贾惜春,王熙凤,贾巧姐…...
稀疏图带负边的全源最短路Johnson算法
BellmanFord算法 Johnson算法解决的问题 带负权的稀疏图的全源最短路 算法流程 重新设置的每条边的权重都大于或等于0,跑完Djikstra后得到的全源最短路,记得要还原,即:f(u,v) d(u,v) - h[u] h[v] 例题...
oracle基础体系
一、 Oracle数据库服务器 数据库在各个行业都会有使用到;其实,我们平时无论是在与客户沟通或者交流中,所说的Oracle数据库是指Oracle数据库服务器(Oracle Server),它由Oracle实例(Oracle Instan…...
k8s运维问题整理
1.宕机或异常重启导致etcd启动失败 服务器非正常关机(意外掉电、强制拔电)后 etcd 数据损坏。 查看apiserver日志发现出现报错Error while dialing dial tcp 127.0.0.1:2379: connect: connection refused,2379是etcd的端口,那么a…...
设计模式(一)设计模式分类和六大设计原则
0.设计模式的分类 GoF提出的设计模式总共有23种,根据目的准则分类分为三大类: 创建型模式,共五种:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰模式…...
git的学习与使用(笔记最全)
什么是git Git是一种分布式版本控制系统,每个开发者都可以在自己的机器上拥有一个完整的仓库 特点 断网也可以工作:没网的情况下,不会影响工作。对于未提交到远程库的代码可以随时撤销。可以查看历史提交记录,以及文件内容的修改记…...
windows环境下Grafana+loki+promtail入门级部署日志系统,收集Springboot(Slf4j+logback)项目日志
🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄 🌹简历模板、学习资料、面试题库、技术互助 🌹文末获取联系方式 📝 往期热门专栏回顾 专栏…...
学习python时一些笔记
1、winr 命令提示符的快捷键 输入cmd进入终端 2、在终端运行桌面上的python文件 cd desktop(桌面) cd是进入该文件夹的意思。 cd .. 回到上一级 运行python时一定要找到文件的所在地 输入python进入,exit()退出%s字符串占位符%d数字占位符%f浮点数占位符input输…...
php基础学习之错误处理(其二)
在实际应用中,开发者当然不希望把自己开发的程序的错误暴露给用户,一方面会动摇客户对己方的信心,另一方面容易被攻击者抓住漏洞实施攻击,同时开发者本身需要及时收集错误,因此需要合理的设置错误显示与记录错误日志 一…...
云计算 2月28号 (linux的磁盘分区)
一 存储管理 主要知识点: 基本分区、逻辑卷LVM、EXT3/4/XFS文件系统、RAID 初识硬盘 机械 HDD 固态 SSD SSD的优势 SSD采用电子存储介质进行数据存储和读取的一种技术,拥有极高的存储性能,被认为是存储技术发展的未来新星。 与传统硬盘相比,…...
demo型xss初级靶场
一、环境 XSS Game - Ma Spaghet! | PwnFunction 二、开始闯关 第一关 看看代码 试一下直接写 明显进来了为什么不执行看看官方文档吧 你不执行那我就更改单标签去使用呗 ?somebody<img%20src1%20onerror"alert(1)"> 防御: innerText 第二关…...
23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
