断点续传本质是HTTP Range请求,需用libcurl等库发HEAD校验Accept-Ranges,设CURLOPT_RANGE为"1024-",响应码为206时以r+b模式seekp定位写入,否则清空重下。

如何用 C++ 发起带 Range 请求的 HTTP 下载
断点续传本质是让服务器只返回文件的一部分,关键在 HTTP 的 Range 请求头。C++ 本身不内置 HTTP 客户端,得依赖第三方库(如 libcurl),不能靠 fopen 或 std::ofstream 直接“接着写”就完事。
libcurl 是最常用选择,它支持 CURLOPT_RANGE 和断点续传所需的底层控制。使用前需确认服务端支持 Accept-Ranges: bytes(可用 curl -I URL 检查)。
-
CURLOPT_RANGE设置格式为"0-1023"或"1024-",后者表示从偏移 1024 到末尾 - 下载前先用 HEAD 请求获取
Content-Length,判断文件总大小是否已知 - 本地文件需以
std::ios::binary | std::ios::in | std::ios::out模式打开,且用seekp()定位到断点位置再写入 - 务必检查响应状态码:206 Partial Content 才代表范围请求成功;如果是 200,说明服务器不支持或忽略
Range
如何安全地读取并追加写入已存在的文件
直接 std::ofstream("file.bin", std::ios::app) 会把数据追加到末尾,但断点续传要求“覆盖写入指定偏移”,不是追加。必须用随机访问模式,并手动定位。
常见错误是打开文件后没校验当前长度,导致 seekp() 超出范围或覆盖错误位置。
立即学习“C++免费学习笔记(深入)”;
- 用
std::ifstream以std::ios::ate打开,调用tellg()获取当前文件大小 - 若本地已有 512KB,而服务器返回的是
Content-Range: bytes 512000-1023999/2048000,则写入起点必须是 512000 字节处 - 写入前用
file.seekp(offset, std::ios::beg),之后每次write()都要检查返回值,避免部分写入 - 不要依赖
std::ofstream::write()的返回值是否等于期望字节数——它不保证全部写入,需配合gcount()或循环重试
如何检测并处理 Range 不支持或响应异常
不是所有 HTTP 服务都支持 Range。比如某些 CDN、Nginx 默认配置未开启 accept_ranges on,或后端是 PHP/Python 脚本未处理 HTTP_RANGE 头,就会静默降级为 200 响应。
这时如果还按断点逻辑处理,会导致文件内容错位、损坏。
- 发起 GET 请求前,先发一次 HEAD,检查响应头是否含
Accept-Ranges: bytes - 下载时检查响应码:
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code),非 206 就中止续传逻辑 - 解析
Content-Range头时,用strtok或std::string_view提取数字区间,警惕格式如bytes */12345(表示不可测总量) - 若服务端返回 200 + 全量内容,应清空本地文件,转为完整下载,而非强行续传
libcurl 回调函数里怎么正确处理分块写入和偏移校验
libcurl 的 CURLOPT_WRITEFUNCTION 回调每次收到一段数据,但这段数据不对应你请求的 Range 起始点——它只是 TCP 流的自然分包。你得自己维护已写入字节数,并确保每批数据写入到正确的磁盘位置。
别在回调里反复 seekp(),性能差且易出错;应在回调外预定位一次,然后顺序写入。
- 把文件句柄和当前期望写入偏移作为回调的
userdata传入,避免全局变量 - 回调内先检查本次待写入长度是否超出预期剩余长度(比如请求了 1MB,但已收 1.1MB),防止越界
- 用
file.write(ptr, size)后立刻检查file.bad() || file.fail(),失败时需记录错误并中断 - 写入完成后,用
file.flush()保证落盘,尤其在嵌入式或低功耗设备上不能省
断点续传真正难的不是发请求,而是把网络流、文件偏移、服务端响应三者严格对齐。少一步校验,文件就可能损坏,而且这种损坏往往到解压或运行时才暴露。


















