拓冰建站拓冰建站
首页 / 资讯中心 / 正文

cpp-httplib 多部分表单上传实战:UploadFormDataItems 内存发送与 make_file_provider 流式上传

后端网络【免费下载链接】cpp-httplibA C header-only HTTP/HTTPS server and client library项目地址https://gitcode.com/GitHub_Trending/cp/cpp-httplib点击查看免费下载multipart/form-data 是浏览器input typefile表单提交文件的唯一标准方式cpp-httplib 为此提供了UploadFormDataItems与FormDataProviderItems两套客户端 API分别面向小文件直接读入内存发送和大文件分块流式上传两种场景。读完本文你将掌握这两套 API 的完整用法、字段含义、混合使用策略以及它们背后在 httplib.h 中的实现原理与服务端接收方式。一、先选型按文件大小决定用哪套 APIcpp-httplib 把 multipart 上传拆成两个概念API 类型数据结构适用场景内存行为UploadFormDataItemsstd::vectorUploadFormData小文件、文本字段整个文件内容先读入std::string一次序列化进请求体FormDataProviderItemsstd::vectorFormDataProvider大文件、视频/日志/压缩包通过回调按 8KB 分块读取文件边读边发选型依据只有一个文件大小。小文件几 MB 以内直接读进内存最省事大文件用 provider 流式发送避免把整个文件加载进内存导致的内存暴涨。两条路径最终都能提交标准的multipart/form-data请求服务端无需区分。两个结构体在 httplib.h 中定义如下struct UploadFormData { std::string name; // 表单字段名如 avatar std::string content; // 字段内容文本字段为字符串文件字段为整个文件字节 std::string filename; // 文件名纯文本字段留空 std::string content_type; // MIME 类型纯文本字段留空 }; using UploadFormDataItems std::vectorUploadFormData; struct FormDataProvider { std::string name; // 表单字段名 ContentProviderWithoutLength provider; // 按需产出数据块的回调 std::string filename; // 上报给服务端的文件名 std::string content_type; // MIME 类型 }; using FormDataProviderItems std::vectorFormDataProvider;可见两者字段几乎一一对应区别只在于UploadFormData直接携带content内容而FormDataProvider携带的是一个数据提供回调。二、小文件上传UploadFormDataItems 一行读完、一次发完小文件的做法是先把文件内容完整读入内存再组装成UploadFormDataItems调用Post#include httplib.h #include fstream #include string httplib::Client cli(http://localhost:8080); // 1. 以二进制模式把整个文件读入内存 std::ifstream ifs(avatar.png, std::ios::binary); std::string content((std::istreambuf_iteratorchar(ifs)), std::istreambuf_iteratorchar()); // 2. 组装 multipart 表单文本字段 文件字段 httplib::UploadFormDataItems items { {name, Alice, , }, // 纯文本字段filename/content_type 留空 {avatar, content, avatar.png, image/png}, // 文件字段 }; // 3. 发送 POST /upload auto res cli.Post(/upload, items); if (res res-status 200) { // 上传成功 }关键点每个UploadFormData条目固定为{name, content, filename, content_type}四元组前两个必填后两个按需填写。纯文本字段只需name和contentfilename与content_type必须留空字符串否则服务端会把它误判为文件。文件内容建议用std::ios::binary模式读取避免文本模式下的换行转换破坏字节。客户端底层会按顺序把每个条目序列化为Content-Disposition: form-data; name...加内容的独立 part因此同一个name可以出现多次同名多文件、同名多文本字段都合法这在 test/test.cc 的MultipartFormDataMultiFileValues测试中有专门验证。三、大文件上传make_file_provider 分块流式发送当文件大到不宜整体驻留内存时改用make_file_provider()。它是 cpp-httplib 内置的文件流式提供器工厂函数实现位于 httplib.hinline FormDataProvider make_file_provider(const std::string name, const std::string filepath, const std::string filename std::string(), const std::string content_type std::string()) { FormDataProvider fdp; fdp.name name; fdp.filename filename.empty() ? filepath : filename; fdp.content_type content_type; fdp.provider filepath - bool { std::ifstream f(filepath, std::ios::binary); if (!f) { return false; } if (offset 0) { f.seekg(static_caststd::streamoff(offset)); if (!f.good()) { sink.done(); return true; } } char buf[8192]; f.read(buf, sizeof(buf)); auto n static_castsize_t(f.gcount()); if (n 0) { return sink.write(buf, n); } sink.done(); // EOF return true; }; return fdp; }使用示例httplib::Client cli(http://localhost:8080); // 文本字段仍然走 UploadFormDataItems httplib::UploadFormDataItems items { {name, Alice, , }, }; // 大文件走 FormDataProviderItems流式发送 httplib::FormDataProviderItems provider_items { httplib::make_file_provider(video, large-video.mp4, , video/mp4), }; // 两个集合可以一起传给同一个 Post auto res cli.Post(/upload, httplib::Headers{}, items, provider_items);make_file_provider()的四个参数依次为参数含义说明name表单字段名对应 part 的Content-Disposition: form-data; name...filepath本地文件路径每次回调都会重新以二进制方式打开文件filename上报给服务端的文件名留空时自动用filepath本身源码第 1564 行filename.empty() ? filepath : filenamecontent_typeMIME 类型如video/mp4、application/octet-stream留空则 part 不带Content-Type实现细节值得注意8KB 固定缓冲char buf[8192]provider 每次回调读取最多 8192 字节写入DataSink内存占用恒定与文件大小无关。支持 offset 续读回调签名带size_t offsetmake_file_provider用它seekg定位后从该位置继续读。这意味着底层传输支持断点续传类逻辑如连接中断后以相同 offset 重试同时也说明 provider 是可重入的——每次调用都重新打开文件再定位。EOF 时调用sink.done()通知客户端该 part 结束文件打不开时返回false让客户端判定失败。ContentProviderWithoutLength不承诺Content-Length因此大文件上传使用 chunked 传输或客户端自行计算边界无需提前扫描文件求长度。四、混合使用文本字段放内存文件走流式文档明确给出的最佳实践是同一个请求里两类集合可以混用推荐把文本字段全部放进UploadFormDataItems把文件全部放进FormDataProviderItems。这正是第三节示例展示的形态对应的客户端重载在 httplib.hResult Post(const std::string path, const Headers headers, const UploadFormDataItems items, const FormDataProviderItems provider_items, UploadProgress progress nullptr);所有 part 会按items在前、provider_items在后的顺序组装进同一个 multipart 请求体服务端按顺序解析不会混淆。五、源码深挖boundary 生成与请求体序列化客户端发送 multipart 的完整链条值得拆解boundary 自动生成默认构造MultipartFormDataWriter时调用detail::make_multipart_data_boundary()httplib.h生成形如--cpp-httplib-multipart-data- 16 位随机字符串的分隔符并自动组装Content-Type: multipart/form-data; boundary...serialize_multipart_formdata_get_content_typehttplib.h。如果你需要自控 boundary例如对接只认特定分隔符的旧服务可用带boundary参数的Post重载httplib.h。逐 part 序列化MultipartFormDataWriterhttplib.h提供serialize、content_length、item_begin、item_end、finish等成员httplib.hUploadFormDataItems整体被编码为符合 RFC 7578 的多 part 请求体。字段转义part 的name/filename遵循 WHATWG 标准转义规则→%22、CR →%0D、LF →%0A见escape_multipart_fieldhttplib.h与浏览器行为保持一致。六、服务端如何接收MultipartFormData 与上传示例客户端发出去的 multipart 请求服务端通过Request的is_multipart_form_data()httplib.h和req.formMultipartFormData类型httplib.h接收。仓库自带的 example/upload.cc 是一个完整的HTML 表单 服务端接收示例页面用原生fetchFormData提交服务端在Post处理器里这样取文件svr.Post(/post, [](const Request req, Response res) { const auto image_file req.form.get_file(image_file); const auto text_file req.form.get_file(text_file); std::cout image file length: image_file.content.length() \n image file name: image_file.filename \n text file length: text_file.content.length() \n text file name: text_file.filename \n; // ... 校验文件名后写入磁盘 });MultipartFormData提供的访问接口均在 httplib.h 声明文本字段get_field(key)、get_fields(key)、has_field(key)、get_field_count(key)文件get_file(key)、get_files(key)、has_file(key)、get_file_count(key)内部容器fieldsFormFields与filesFormFiles均使用保持插入顺序的 multimap保证按客户端发送顺序读取见 httplib.h 的注释RFC 7578 5.2 要求 form processor 按顺序返回结果。服务端安全实践文件名清洗example/upload.cc 特别演示了文件名净化从客户端传来的filename中剥离路径部分并拒绝空名、.、..、含冒号Windows 盘符的名字否则直接返回 400。multipart 的filename完全由客户端控制落地到磁盘前必须做同样的清洗防止路径穿越。流式接收大文件ContentReader如果服务端也要避免把大文件整块读进内存应使用ContentReader形式的重载按 part 分块消费。测试 test/test.cc 展示了这一模式svr.Post(/upload, { ASSERT_TRUE(req.is_multipart_form_data()); std::vectorFormData items; content_reader( { // 每个 part 开始 items.push_back(file); return true; }, { // 每个 part 的数据块 items.back().content.append(data, n); return true; }); });七、测试验证官方如何保证这两套 API 的正确性仓库的 gtest 测试对本文涉及的两套 API 都有直接覆盖可当作可运行的最小验证代码test/test.ccMultipartFormData一次上传两个文本字段含 UTF-8 内容aωb 两个文件含空 content_type 与带前后空格的 content_type验证服务端正确解析。test/test.ccMultipartFormDataMultiFileValues同名 text 字段两个值、同名 file 字段两个文件验证多值语义。test/test.ccMakeFileProvider创建一个 4096 字节的临时二进制文件用make_file_provider(myfile, tmp_path, data.bin, application/octet-stream)上传断言服务端收到的name、filename、content_type与文件内容逐字节一致——这是验证FormDataProviderItems流式路径正确性的核心用例。test/test.ccExcessivePartHeaders构造 101 个 part 头超过默认上限 100断言服务端返回 400提示了 multipart 解析的安全边界。八、补充能力与延伸阅读上传进度回调两类Post重载都支持最后一个可选参数UploadProgress progress nullptr可用于显示上传进度具体用法见文档 C11. 使用进度回调。PUT / PATCH 同样支持Put、Patch提供了与Post完全对等的 multipart 重载httplib.h、httplib.h同一套UploadFormDataItems/FormDataProviderItems可无缝换用。普通文件体上传非 multipart如果不需要表单字段、只想裸传一个文件体可用make_file_body()httplib.h它返回(文件大小, ContentProvider)并配合Content-Length使用——注意它与 multipart 流式上传是两条独立路径make_file_provider走 multipartmake_file_body走纯 body。相关文档C10. 流式响应、服务端流式响应指南。小结一句话选型小文件用UploadFormDataItems读入内存后一次Post发出大文件用make_file_provider()生成的FormDataProviderItems以 8KB 分块流式上传两者可混用文本字段归内存、文件归流式。理解 httplib.h 中UploadFormData、FormDataProvider、make_file_provider与MultipartFormDataWriter的实现你就能精确预测每种参数组合产生的 wire 格式并为自己的上传场景定制 provider 回调例如从数据库、内存缓存或远程源流式取数把 cpp-httplib 的 multipart 能力用到极致。赞分享后端网络【免费下载链接】cpp-httplibA C header-only HTTP/HTTPS server and client library项目地址https://gitcode.com/GitHub_Trending/cp/cpp-httplib点击查看免费下载相关推荐cpp-httplib 流式上传实战用 ContentProviderWithoutLength 发送 HTTP Chunked 传输正文cpp httplib 流式上传实战用 ContentProviderWithoutLength 发送 HTTP Chunked 传输正文 本文基于 cpp后端网络cpp-httplib 基础客户端实战用 httplib::Client 发送 GET / POST 与文件上传cpp httplib 基础客户端实战用 httplib::Client 发送 GET / POST 与文件上传 cpp httplib 不仅是一个 head后端网络cpp-httplib 流式接收 Multipart 上传HandlerWithContentReader 实战指南cpp httplib 流式接收 Multipart 上传HandlerWithContentReader 实战指南 导读 本文围绕 cpp httplib后端网络创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门