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://xiahunao.cn/news/2814570.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【生态适配】亚信安慧AntDB数据库与FT-2000+/64处理器完成兼容互认

日前&#xff0c;亚信安慧AntDB数据库完成了与FT-2000/64处理器的兼容互认。经湖南亚信安慧科技有限公司&#xff08;简称“亚信安慧”&#xff09;与飞腾信息技术有限公司&#xff08;简称“飞腾公司”&#xff09;的严格测试&#xff0c;亚信安慧AntDB数据库V6.2在FT-2000/64…

《大模型时代-ChatGPT开启通用人工智能浪潮》精华摘抄

原书很长&#xff0c;有19.3w字&#xff0c;本文尝试浓缩一下其中的精华。 知识点 GPT相关 谷歌发布LaMDA、BERT和PaLM-E&#xff0c;PaLM 2 Facebook的母公司Meta推出LLaMA&#xff0c;并在博客上免费公开LLM&#xff1a;OPT-175B。 在GPT中&#xff0c;P代表经过预训练(…

一看就会:使用nvm实现多个版本的node自由切换

一、介绍 使用nvm可以方便的在同一台设备上进行多个node版本之间切换&#xff0c;解决不同的项目所使用的node版本不一样的问题 二、安装nvm 如果已安装node环境先卸载后再安装nvm&#xff0c;防止出现不确定错误 1、卸载node环境&#xff0c;并清除node环境变量配置 通过…

【README 小技巧】 展示gitee中开源项目start

【README 小技巧】 展示gitee中开源项目start <a target"_blank" hrefhttps://gitee.com/wujiawei1207537021/wu-framework-parent><img srchttps://gitee.com/wujiawei1207537021/wu-framework-parent/badge/star.svg altGitee star/></a>

我在使用 Copilot 时遇到了许可证验证错误。

如果使用的是 Copilot&#xff0c;并收到以下错误消息&#xff0c;请按以下步骤进行操作&#xff1a; We encountered a problem validating your Copilot license. For more information, see https://aka.ms/copilotlicensecheck 请确保使用的是正确的帐户 请确保已使用具…

Flink动态分区裁剪

1 原理 1.1 静态分区裁剪与动态分区裁剪 静态分区裁剪的原理跟谓词下推是一致的&#xff0c;只是适用的是分区表&#xff0c;通过将where条件中的分区条件下推到数据源达到减少分区扫描的目的   动态分区裁剪应用于Join场景&#xff0c;这种场景下&#xff0c;分区条件在joi…

kafka平滑升级过程指导

一、前言 Apache Kafka作为常用的开源分布式流媒体平台&#xff0c;可以实时发布、订阅、存储和处理数据流,多用于作为消息队列获取实时数据&#xff0c;构建对数据流的变化进行实时反应的应用程序&#xff0c;已被数千家公司用于高性能数据管道、流分析、数据集成和任务关键型…

算法day01_ 27. 移除元素、977.有序数组的平方

推荐阅读 从零开始学数组&#xff1a;深入浅出&#xff0c;带你掌握核心要点 初探二分法 再探二分法 系统的纪录一下刷算法的过程&#xff0c;之前一直断断续续的刷题&#xff0c;半途而废&#xff0c;现在重新开始。话不多说&#xff0c;开冲&#xff01; 27.移除元素 题目 给…

js 面试 什么是WebSockets?HTTP和HTTPS有什么不同?web worker是什么?

概念&#xff1a; webSocket 是一种在客户端和服务端之间建立持久连接的协议&#xff0c;它提供全双工通信通道&#xff0c;是服务器可以主动向客户端推送数据&#xff0c;同时也可以接受客户端发送的数据。 1 webSocket与https区别&#xff1f; 在网络通信中&#xff0c;We…

Acceptor监听套接字管理类实现(模块七)

目录 类功能 类定义 类实现 编译测试 类功能 类定义 // 监听套接字管理类 class Acceptor { private:Socket _socket; // 用于创建监听套接字EventLoop *_loop; // 用于对监听套接字进行事件监控Channel _channel; // 用于对监控套接字进行事件管理using AcceptCallback…

11 PLL IP核

PLL IP 核简介 锁相环&#xff08;PLL&#xff09;作为一种反馈控制电路&#xff0c;其特点是利用外部输入的参考信号来控制环路内部震荡信号的频率和相位。因为锁相环可以实现输出信号频率对输入信号频率的自动跟踪&#xff0c;所以锁相环通常用于闭环跟踪电路。锁相环在工作…

36.云原生之SpringCloud+k8s实践

云原生专栏大纲 文章目录 SpringCloudk8s介绍spring-cloud-kubernetes服务发现配置管理负载均衡选主 spring-cloud-bookinfo案例构建项目环境配置namespace部署与验证productpagegatewaybookinfo-admindetailsratingsreviewsreviews-v1reviews-v2 总结 SpringCloudk8s介绍 ht…

React UI框架Antd 以及 如何按需引入css样式配置(以及过程中各种错误处理方案)

一、react UI框架Antd使用 1.下载模块 npm install antd -S 2.引入antd的样式 import ../node_modules/antd/dist/reset.css; 3.局部使用antd组件 import {Button, Calendar} from antd; import {PieChartTwoTone} from ant-design/icons; {/* 组件汉化配置 */} import l…

SORA 到底是什么?如何用bitget wallet购买?

什么是SORA&#xff1f; SORA 是一种模因币&#xff0c;灵感来自 OpenAI 最新的人工智能模型 Sora&#xff0c;它巧妙地根据文本输入生成视频。 SORA 诞生于加密社区内人工智能项目的热潮中&#xff0c;利用 OpenAI 的公告推出了一种独特且时尚的数字资产。正如 memecoin 网站…

【管理咨询宝藏资料28】某信息技术有限公司战略规划报告

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏资料28】某信息技术有限公司战略规划报告 【关键词】战略规划、对标研究、管理咨询 【文件核心观点】 - 使企业实现商业流程整合&#xff0c;构…

宏集小型PLC应用于浮动封盖机

导言 仅通过1个控制面板和1个紧凑型PLC控制自动化设备中来自不同制造商的13种不同电机&#xff0c;听起来难以置信&#xff01;但这是宏集科技已经落地的合作项目&#xff01; 案例概况 客户&#xff1a;TREPAK 合作伙伴&#xff1a;SDT 应用&#xff1a;封盖机 应用产品&…

UE5 UE4 自定义插件自动开启关联插件(plugin enable)

在我们自己编写UE4、UE5的插件时&#xff0c;常常需要开启相关联的插件进行功能编写。 例如&#xff1a;UE4/5 批量进行贴图Texture压缩、修改饱和度_ue4批量修改纹理大小-CSDN博客 而让插件使用者每次使用时&#xff0c;依次进行开启其他相关联插件确实有些麻烦。 如何只需要…

【数据结构】数组

第一章、为什么数组的下标一般从0开始编号 提到数组&#xff0c;读者肯定不陌生&#xff0c;甚至还会很自信地说&#xff0c;数组很简单。编程语言中一般会有数组这种数据类型。不过&#xff0c;它不仅是编程语言中的一种数据类型&#xff0c;还是基础的数据结构。尽管数组看起…

js 面试 http 与 https, 长连接,http状态码,哪三次握手,在浏览器地址栏键入URL回车之后过程?

1 概念 http是超文本传输协议&#xff0c;信息明文传输。 https是安全超文本传输协议, 以安全为目标的HTTP通道,在HTTP的基础上通过身份认证和传输加密阶段保证了传输过程的安全性. https:主要是增加了TLS&#xff08;Transport Layer Security 安全传输层协议&#xff09;/…

超声波清洗机对眼镜有伤害吗?警惕清洗眼镜禁忌!

近年来&#xff0c;随着人们对健康生活要求的提升&#xff0c;超声波清洗机在市场上的受欢迎程度逐渐攀升&#xff0c;产品的多样性也让人眼花缭乱。近期收到了大量读者的一些私信&#xff0c;其中很多人询问使用超声波清洗机会对眼镜有伤害吗、超声波清洗机是不是智商税、超声…