ARTICLE DETAIL

资讯详情

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

cpp-httplib 进度回调完全指南:DownloadProgress 与 UploadProgress 的实现原理、取消机制与实战用法

cpp-httplib 进度回调完全指南:DownloadProgress 与 UploadProgress 的实现原理、取消机制与实战用法 后端网络【免费下载链接】cpp-httplibA C header-only HTTP/HTTPS server and client library项目地址https://gitcode.com/GitHub_Trending/cp/cpp-httplib点击查看免费下载cpp-httplib 是一个仅头文件header-only的 C HTTP/HTTPS 客户端与服务端库。当你的应用需要向用户展示正在下载 xx%正在上传 xx%这类实时进度时无需引入任何第三方 UI 库——httplib::Client内建了DownloadProgress与UploadProgress两种回调一个(current, total)参数对即可完成下载/上传进度跟踪、百分比计算甚至实现取消按钮式的传输中止。读完本文你将掌握这两种回调的完整用法、total缺失时的防御写法、ContentReceiver与进度回调的搭配方式以及如何基于回调返回值安全地取消进行中的传输。一、回调签名与触发时机先看类型定义在 httplib.h 中两种进度回调的类型定义完全一致using DownloadProgress std::functionbool(size_t current, size_t total); using UploadProgress std::functionbool(size_t current, size_t total);两个参数current是当前已传输的字节数total是传输的总字节数返回值bool返回true表示继续传输返回false表示中止传输调用线程回调在客户端进行网络 I/O 的线程内同步调用因此不要在回调里执行耗时操作也不要在回调里直接操作需要线程安全的共享对象如需跨线程共享标志位请使用std::atomic下文取消传输一节有示例。进度回调的触发时机是每收到/每发送一批数据就回调一次而不是每秒回调一次。因此对于大文件回调可能被调用非常多次回调体应当保持轻量仅做百分比计算、打印或写入一个原子标志位。二、下载进度接收 Content-Length 与百分比计算在httplib::Client上调用Get()时传入一个DownloadProgress回调即可httplib::Client cli(http://localhost:8080); auto res cli.Get(/large-file, [](size_t current, size_t total) { auto percent (total 0) ? (current * 100 / total) : 0; std::cout \rDownloading: percent % ( current / total ) std::flush; return true; // return false to abort }); std::cout std::endl;关键细节total来源于Content-Length响应头。如果服务器没有返回Content-Length例如使用Transfer-Encoding: chunked分块传输total可能为0。此时无法计算百分比示例代码用(total 0) ? ... : 0做了防御退化为仅显示已接收字节数current。\r配合std::flush实现单行覆盖式刷新适合命令行终端GUI 程序则应在回调里更新进度条控件的值。返回false会中止下载此时调用返回的Result中res.error()为Error::Canceled详见下文。从源码看下载进度回调的调用链从 httplib.h 的重载声明可以看到DownloadProgress被接入到Get()的多个重载中从最简形式到完整形式一应俱全Result Get(const std::string path, DownloadProgress progress nullptr); Result Get(const std::string path, ContentReceiver content_receiver, DownloadProgress progress nullptr); Result Get(const std::string path, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress nullptr); Result Get(const std::string path, const Headers headers, DownloadProgress progress nullptr); Result Get(const std::string path, const Params params, const Headers headers, ResponseHandler response_handler, ContentReceiver content_receiver, DownloadProgress progress nullptr);也就是说进度回调可以和自定义请求头Headers、查询参数Params、响应头回调ResponseHandler、流式接收器ContentReceiver任意组合参数顺序始终是progress位于最后一个位置。值得注意的是仓库测试 test/test.cc 中特别注释到DownloadProgress回调仅对带Content-Length的响应触发——测试用 httpbingo 的/bytes/524288这类固定长度端点来保证回调在整段 body 收完之前被触发从而给取消留出机会。这印证了total的取值来源也提醒我们如果你的服务端用 chunked 编码响应进度回调可能一次都不会被调用此时应改用ContentReceiver做已接收字节数的累计显示。三、上传进度Post / Put / Patch 与多重重载上传与下载对称把UploadProgress作为Post()或Put()的最后一个参数传入即可httplib::Client cli(http://localhost:8080); std::string body load_large_body(); auto res cli.Post(/upload, body, application/octet-stream, [](size_t current, size_t total) { auto percent current * 100 / total; std::cout \rUploading: percent % std::flush; return true; }); std::cout std::endl;与下载不同上传时total通常总是可靠的——因为客户端在发起请求前就知道 body 的长度std::string的大小、ContentProvider声明的内容长度或 multipart 表单的长度它会写入Content-Length请求头。因此上传场景一般不需要total 0的防御判断不过从源码结构看ContentProviderWithoutLength这类长度未知的重载存在遇到未知长度时仍需谨慎处理。哪些重载支持 UploadProgress从 httplib.h 的声明看UploadProgress出现在以下几种上传形态的末尾Post(path, body, content_type, UploadProgress)/Put(path, body, content_type, UploadProgress)字符串 body最常用Post(path, content_length, ContentProvider, content_type, UploadProgress)自定义内容提供者流式/文件上传Post(path, ContentProviderWithoutLength, content_type, UploadProgress)无长度提供者Post(path, UploadFormDataItems, UploadProgress)multipart 表单上传带Headers的对应全部变体以及Put的同名重载Patch同样支持进度回调测试 test/test.cc 中TestStringBodyUploadProgress对Post/Put/Patch三种方法做了统一验证。测试用例如何验证上传进度仓库测试 test/test.cc 中的TestContentProviderUploadProgress给出了一个很有参考价值的验证模式在回调里把每次的current值收集进std::vectoruint64_t传输结束后断言progress_values非空、且请求成功res-status 200。multipart 表单上传也有对应的TestMultipartUploadProgresstest/test.cc用包含文本字段与文件字段的UploadFormDataItems验证回调被多次触发。如果你要自测自己的上传进度逻辑可以照此模式在回调中累计current再对比最终发送的总字节数是否一致。四、取消传输回调返回 false 与原子标志位进度回调的bool返回值承担了取消职责返回false传输即中止。文档给出的经典 UI 场景是取消按钮——按钮事件置位一个标志下一次进度回调读取到该标志就返回falsestd::atomicbool cancelled{false}; auto res cli.Get(/large-file, { return !cancelled.load(); });注意事项由于回调在 I/O 线程中同步执行而取消按钮通常在 UI 线程标志位必须用std::atomic避免数据竞争文档示例即是如此。取消的粒度是下一次进度回调回调只有在收到下一批数据时才会被触发所以取消不是瞬时的最多滞后一个数据块对 Content-Length 响应通常为 8 KB 左右的缓冲块。取消后res不为空但res-status无效正确做法是检查res.error()。在 httplib.h 附近的响应读取逻辑中进度回调返回false时错误被置为Error::Canceled其对应的人类可读描述为Connection handling canceledhttplib.h。测试用例对取消行为的验证仓库测试 test/test.cc 的CancelTest系列精确验证了这一行为NoCancel_Online回调恒返回true请求成功且 body 完整WithCancelSmallPayload_Online回调恒返回false断言!res成立且res.error() Error::Canceled。测试注释明确指出取消测试必须选用/bytes/524288这类大 payload因为DownloadProgress回调只对带 Content-Length 的响应触发payload 太小的话回调在 body 收完前根本不会被调用取消也就无从谈起。这再次印证了回调触发频率取决于网络数据到达节奏这一本质。五、与 ContentReceiver 搭配边流式保存边显示进度进度回调只负责报数数据本身不会交付给回调。如果你既要下载进度又要边下载边把数据写进文件而不是等整段 body 收完再一次性拿到res-body就需要同时传入ContentReceiver和DownloadProgresshttplib::Client cli(http://localhost:8080); std::ofstream ofs(output.bin, std::ios::binary); if (!ofs) { std::cerr Failed to open file std::endl; return 1; } auto res cli.Get(/large-file, { // ContentReceiver逐块收数据 ofs.write(data, len); return static_castbool(ofs); // 写盘失败即中止下载 }, [](size_t current, size_t total) { // DownloadProgress逐块报进度 auto percent (total 0) ? (current * 100 / total) : 0; std::cout \rDownloading: percent % std::flush; return true; });从 httplib.h 的类型定义看ContentReceiver的签名是using ContentReceiver std::functionbool(const char *data, size_t data_length);它把响应 body 切成一块块数据data/data_length交给你的回调返回false同样可以中止下载。两种回调在Get()重载中的位置固定为content_receiver在前、progress在后。此外还有带ResponseHandler的三回调版本——你可以在响应头到达后、body 开始前先读取Content-Length等信息参见配套文档 C01. Get the response body / save to a file其中给出了用ResponseHandler提前打印文件大小的完整示例。需要区分的是 httplib.h 中另一个类型ContentReceiverWithProgress——它的签名是bool(const char *data, size_t data_length, size_t offset, size_t total_length)把数据块、块内偏移、总长度合并进同一个回调与ContentReceiverDownloadProgress双回调是两种不同的风格可按需选用。六、实战要点小结场景推荐做法关键注意事项显示下载百分比cli.Get(path, DownloadProgress)total来自Content-Length缺失时可能为0需防御处理显示上传百分比cli.Post/Put/Patch(..., UploadProgress)客户端自知 body 长度total通常可靠大文件边下边写cli.Get(path, ContentReceiver, DownloadProgress)ContentReceiver负责落盘进度回调只负责展示UI 取消按钮回调返回!cancelled.load()标志位必须用std::atomic取消滞后最多一个数据块判断传输是否被取消检查res.error() httplib::Error::Canceled不要依赖res-status此时 status 无效进度回调的触发频率取决于网络数据块的到达/发送节奏而非时间因此回调体内不要做重活对 chunked 响应不要依赖DownloadProgress可用ContentReceiver自行累计取消传输后务必通过Error::Canceled描述为Connection handling canceled判断结果。只要遵循这些原则cpp-httplib 的进度回调就能为你的下载器、上传器或桌面 GUI 提供稳定、可取消的实时进度能力。赞分享后端网络【免费下载链接】cpp-httplibA C header-only HTTP/HTTPS server and client library项目地址https://gitcode.com/GitHub_Trending/cp/cpp-httplib点击查看免费下载相关推荐dotnet-script内联NuGet包使用详解快速集成第三方库的完整教程dotnet script内联NuGet包使用详解快速集成第三方库的完整教程 dotnet script是一款强大的工具允许开发者直接从.NET CLI运行开发工具CLIlaravel-admin Model Form 回调Callback机制完全指南submitted / saving / saved 的调用时机与实战用法laravel admin Model Form 回调Callback机制完全指南submitted / saving / saved 的调用时机与实战用后端低代码前端cpp-httplib 流式 API 实战用 stream::Get 与 open_stream 实现逐块读取、SSE 与反向代理cpp httplib 流式 API 实战用 stream::Get 与 open_stream 实现逐块读取、SSE 与反向代理 本篇文章以 cpp htt后端网络创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表