续传需先检查文件存在且大小>0,再head请求比对content-length,若本地≥远程则完成,否则用range头(bytes=offset-)发起206请求,并以ios::in|ios::out|ios::ate模式写入指定偏移。

如何用 libcurl 发起带 Range 头的续传请求
断点续传本质是 HTTP 协议层面的支持,不是 C++ 语言特性。核心在于:先查本地文件长度,再用 Range 请求剩余字节。必须确保服务端返回 206 Partial Content,否则说明不支持或配置错误。
实操要点:
- 用
stat()或std::filesystem::file_size()(C++17)获取已下载文件大小,作为Range起始偏移 - 设置 curl 选项:
curl_easy_setopt(handle, CURLOPT_RANGE, "bytes=1024-"),注意格式不能写成"1024-"(少bytes=会导致 416) - 务必关闭
CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST仅用于测试;生产环境需保留证书校验 - 响应码不是
206时,不能直接追加写入,应清空文件重下或报错
文件写入必须用 std::ios::ate | std::ios::in | std::ios::out 模式
普通 std::ofstream 默认覆盖,std::ios::app 又强制 seek 到末尾、无法随机写入。续传需要在指定 offset 写入,唯一可靠方式是打开文件为可读可写+定位模式。
示例关键代码:
std::fstream file(path, std::ios::in | std::ios::out | std::ios::ate);
if (file.is_open()) {
file.seekp(offset); // offset 是已下载字节数
file.write(buffer, size);
}
注意:std::ios::ate 保证初始位置在文件末尾,但 seekp() 后即可任意写;若文件不存在,std::ios::ate 会失败,需先创建空文件。
如何安全判断是否需要续传而非重下
不能只看文件存在与否——可能文件损坏、服务端内容已更新、ETag 不匹配。最简健壮策略是三步验证:
- 检查文件是否存在且大小 > 0
- 发起 HEAD 请求,对比响应头
Content-Length与本地文件大小:若本地 >= 远程,说明已完成;若本地 - (可选增强)比对
ETag或Last-Modified,不一致则删除本地文件重下
忽略 ETag 验证在 CDN 场景下容易下到过期文件,尤其当服务端启用了缓存且资源被更新时。
多线程并发续传会踩哪些坑
同一个文件多个 Range 并发下载,最后拼接时极易出错。C++ 标准库的 std::fstream 不是线程安全的写操作,即使不同线程写不同 offset,也因底层缓冲、文件系统元数据竞争导致数据错乱。
可行方案只有两种:
- 单线程顺序下载:最稳,适合中小文件(
- 每个线程写独立临时文件(如
part_001.tmp),全部完成后再用cat或std::ifstream顺序合并——注意合并时按 Range 顺序拼,别依赖文件名排序
试图用 pread/pwrite + open(O_RDWR) 手动管理 fd 在 Linux 可行,但 Windows 无等价原子接口,跨平台成本高,不推荐初版实现。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











