ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

cpp-httplib 客户端 Chunked Transfer 上传:ContentProviderWithoutLength 与 DataSink 完整实战指南

cpp-httplib 客户端 Chunked Transfer 上传:ContentProviderWithoutLength 与 DataSink 完整实战指南 后端网络【免费下载链接】cpp-httplibA C header-only HTTP/HTTPS server and client library项目地址https://gitcode.com/GitHub_Trending/cp/cpp-httplib点击查看免费下载本篇指南讲解在 cpp-httplib单头文件 C HTTP/HTTPS 客户端/服务端库中如何以chunked transfer encoding分块传输编码发送请求体。当请求体大小事先无法确定、数据需要边生成边发送如实时日志、传感器数据、流式管道输出时使用ContentProviderWithoutLength配合DataSink即可实现真正的流式上传。读完本文你将掌握两种 ContentProvider 的选型与签名差异、sink.write()/sink.done()的返回值契约、make_file_body()的便捷用法以及底层write_content_chunked的分块组装原理。一、什么时候需要 Chunked 上传HTTP 请求体在发送前通常需要确定Content-Length。但存在两类场景无法预先知道总大小数据由程序实时生成例如从摄像头、传感器、生成器函数逐段产出数据从另一个流管道直通过来例如把一个下载流直接转发为上传流来不及也不愿意先落地缓存。此时 cpp-httplib 客户端会改用Transfer-Encoding: chunked发送请求体把数据切成若干块chunk每块带上十六进制长度前缀最后以0\r\n作为终止标记。服务端无需预先知道大小即可边收边处理。在 httplib.h 的send_with_content_provider_and_receiver实现中可以看到当只提供content_provider_without_length而未提供带长度的 provider时库会自动设置req.content_length_ 0; req.content_provider_ detail::ContentProviderAdapter( std::move(content_provider_without_length)); req.is_chunked_content_provider_ true; req.set_header(Transfer-Encoding, chunked);也就是说选择不带长度的 provider库就自动切换为 chunked 编码并写入Transfer-Encoding: chunked头你无需手动设置任何请求头。二、核心类型DataSink 与两种 ContentProviderDataSink上传数据的下水道DataSink定义在 httplib.h是 provider 回调与底层 socket 之间的桥梁。它的关键成员如下成员签名说明writestd::functionbool(const char *data, size_t data_len)唯一必须使用的成员把一段数据交给连接发送返回是否成功is_writablestd::functionbool()默认返回true可用于判断连接当前是否可写donestd::functionvoid()默认空操作调用后标记数据发送完毕触发终止块0\r\ndone_with_trailerstd::functionvoid(const Headers trailer)默认转调done()支持在末尾追加 trailer 头字段osstd::ostream便捷的流式输出接口内部转发到write注意DataSink被设计为不可拷贝、不可移动拷贝/移动构造与赋值均被delete因此 provider 捕获其指针sink是安全的见 httplib.h 中的注释。两种 ContentProvider 的类型别名在 httplib.h 中using ContentProvider std::functionbool(size_t offset, size_t length, DataSink sink); using ContentProviderWithoutLength std::functionbool(size_t offset, DataSink sink);ContentProvider带length参数配合已知的总大小使用走Content-LengthContentProviderWithoutLength不提供长度走 chunked。两者的bool返回值语义一致返回true表示继续返回false表示请求已终止例如检测到连接断开。三、基础用法不知道大小边生成边发这是原文档 c09-chunked-upload.md 的核心示例httplib::Client cli(http://localhost:8080); auto res cli.Post(/stream, { std::string chunk produce_next_chunk(); if (chunk.empty()) { sink.done(); // done sending return true; } return sink.write(chunk.data(), chunk.size()); }, application/octet-stream);这个 lambda 的职责只有两件事产出下一块数据通过sink.write(chunk.data(), chunk.size())发送没有更多数据时调用sink.done()结束发送。要点offset参数表示当前已累计发送的字节数可用于进度统计或日志注意 chunked 模式下length参数不存在被替换为这一偏移量chunk.empty()被用作流结束信号这是由你的 producer 逻辑决定的约定——返回空串即代表没有下一块了sink.write()返回bool若为false说明连接已断开lambda 应返回false停止发送。原文档特别强调这一点If it returnsfalse, the connection is gone — returnfalsefrom the lambda to stop.在底层 write_content_chunked 的实现中每次sink.write()的数据会被封装为一个标准 chunkauto chunk from_i_to_hex(payload.size()) \r\n payload \r\n; if (!write_data(strm, chunk.data(), chunk.size())) { ok false; }即每个块由十六进制字节长度 CRLF 块数据 CRLF组成而sink.done()会触发终止标记constexpr const char done_marker[] 0\r\n; if (!write_data(strm, done_marker, str_len(done_marker))) { ok false; }主循环见 httplib.h反复调用 provider直到data_available变为false即done()被调用while (data_available !is_shutting_down()) { if (!strm.wait_writable() || !strm.is_peer_alive()) { error Error::Write; return false; } else if (!content_provider(offset, 0, data_sink)) { error Error::Canceled; // provider 主动返回 false → 取消 return false; } else if (!ok) { error Error::Write; // sink.write 返回 false → 写入失败 return false; } }可见 provider 返回false被映射为Error::Canceledsink.write()失败则映射为Error::Write——两种失败途径在错误码层面被区分开便于排查。四、已知总大小优先使用 ContentProvider如果事先就知道请求体总大小应改用带长度的ContentProvider重载并把total_size一并传入原文档示例size_t total_size get_total_size(); auto res cli.Post(/upload, total_size, { auto data read_range(offset, length); return sink.write(data.data(), data.size()); }, application/octet-stream);此时请求携带Content-Length头而不是Transfer-Encoding: chunked服务端因此可以计算并展示上传进度。原文档的结论是Prefer this form when you can.能确定大小就优先用它。两种方式的取舍可以归纳为场景使用类型请求头服务端进度大小未知 / 边生成边发ContentProviderWithoutLengthTransfer-Encoding: chunked无法按总长度计算进度大小已知ContentProvider带lengthContent-Length可按总长度上报进度发送文件的终极捷径make_file_body()若已知大小的场景就是发送本地文件无需手写 provider。make_file_body()定义在 httplib.h它一次性完成两件事用std::ifstream以ate模式打开文件读取tellg()得到文件字节数返回一个按offset/length分片读取的ContentProvider内部按 8192 字节缓冲循环f.read并校验文件实际长度与承诺的Content-Length一致若中途读到 EOF 则返回false终止。用法即原文档所指If youre just sending a file,make_file_body()is easier. See C08. POST a file as raw binary.auto [size, provider] httplib::make_file_body(/path/to/data.bin); auto res cli.Post(/upload, size, provider, application/octet-stream);完整的多文件/表单上传场景还可参考make_file_provider()httplib.h与get_multipart_content_providerhttplib.h见菜谱 C07. Upload a file as multipart form data。五、API 全景Post / Put / Patch 的重载家族ContentProviderWithoutLength并不是只有Post一个入口。在 httplib.hClient与 httplib.hClientImpl中Post、Put、Patch三个方法都提供了一整套对称重载// 最简形式 Result Post(const std::string path, ContentProviderWithoutLength content_provider, const std::string content_type, UploadProgress progress nullptr); // 带自定义请求头 Result Post(const std::string path, const Headers headers, ContentProviderWithoutLength content_provider, const std::string content_type, UploadProgress progress nullptr); // 带响应体接收器progress 变为 DownloadProgress Result Post(const std::string path, ContentProviderWithoutLength content_provider, const std::string content_type, ContentReceiver content_receiver, UploadProgress progress nullptr);Put 与 Patch 的重载结构与此完全一致。其中Headers参数可附加Authorization、自定义元数据等头字段UploadProgress回调可跟踪上传进度签名形如bool(uint64_t current, uint64_t total)chunked 模式无总长度时按累计字节上报配合菜谱 C11. Use the progress callback 使用ContentReceiver用于同时接收响应体流可参考菜谱 C10. Receive a response as a stream。六、底层原理chunked 如何在库内完成客户端ContentProviderAdapter 适配客户端把无长度 provider 包装为内部统一的有长度签名适配器定义在 httplib.hclass ContentProviderAdapter { public: explicit ContentProviderAdapter(ContentProviderWithoutLength content_provider) : content_provider_(std::move(content_provider)) {} bool operator()(size_t offset, size_t, DataSink sink) { return content_provider_(offset, sink); } ... };length参数被忽略转发为content_provider_(offset, sink)。请求发送侧的分块写出由 write_content_chunked 调用响应接收侧由 httplib.h 调用。支持压缩、Trailer 与断连检测write_content_chunkedhttplib.h除了组装分块还内置了三项能力逐块压缩每块数据先经compressor.compress()处理压缩后的载荷再进入 chunk 封装因此 chunked 可与 Content-Encoding 压缩共存Trailer 字段sink.done_with_trailer(Headers)可在终止块0\r\n之后追加 trailer 头如Checksum、签名摘要写出前会校验字段名/值合法性以防 CR/LF 注入可写性与存活检测每轮循环前调用strm.wait_writable()与strm.is_peer_alive()DataSink::is_writable被绑定为strm.is_peer_alive()见 httplib.h连接异常时以Error::Write提前终止避免阻塞在已死连接上。七、服务端视角对应能力一览虽然本文聚焦客户端上传但 cpp-httplib 服务端也提供对称的分块能力便于理解全链路Response::set_chunked_content_provider(content_type, ContentProviderWithoutLength, resource_releaser)httplib.h用于服务端以 chunked 流式返回响应体配合菜谱 S05. Stream a large file in the responseResponse::set_content_provider的两种重载httplib.h分别对应有长度/无长度两种 provider无长度版本同样经由ContentProviderAdapter转换服务端对传入请求的Transfer-Encoding/Content-Length冲突做了严格校验见 httplib.h 与 httplib.h 中引用的 RFC 9112 §6 规则。八、实践小结与避坑清单选型能提前知道大小就用带length的ContentProvider服务端可报进度否则用ContentProviderWithoutLength库自动写Transfer-Encoding: chunked结束信号chunked 模式必须在数据发完后调用sink.done()或done_with_trailer否则主循环会一直等待空字符串作为无下一块的判定由你的 producer 约定返回值纪律sink.write()返回false说明连接已断lambda 应立即返回false停止provider 返回false被映射为Error::Canceled与Error::Write区分发送文件优先使用make_file_body()一行获取(size, provider)对省去手写分片逻辑进度上传进度回调UploadProgress在 chunked 模式下按累计字节推进无法换算为百分比需要与服务端配合或自行统计offset。更多客户端技巧可继续阅读 Cookbook 总索引 中的 C10. Receive a response as a stream、C11. Use the progress callback 以及 C08. POST a file as raw binary。赞分享后端网络【免费下载链接】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 客户端默认请求头set_default_headers() 完整指南cpp httplib 客户端默认请求头set_default_headers 完整指南 导读 在调用第三方 API 或自建后端时几乎每个请求都需要携带固后端网络创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表