断点续传本质是http range请求+本地文件偏移写入,需服务端支持206响应与accept-ranges头,客户端用'ab'模式追加写入并校验content-range与哈希。

断点续传本质是 HTTP Range 请求 + 本地文件偏移写入
断点续传不是 Python 独有的魔法,它依赖服务端支持 Range 头和返回 206 Partial Content,客户端则要跳过已下载字节、从指定位置追加写入。Python 标准库没封装“断点续传”这个动作,得自己拼:requests 发带 Range 的请求 + 手动控制 open(..., 'ab') 模式写入。
常见错误现象:用 'wb' 模式覆盖重写、没校验服务端是否真返回了 206(有些 CDN 或 Nginx 默认不返回)、忽略 Content-Range 响应头里的真实起始偏移,导致文件错位。
- 务必先读取本地文件长度,作为下次请求的
Range起始值:start = os.path.getsize(local_path) - 发请求前检查服务端是否支持断点:
response.headers.get('Accept-Ranges') == 'bytes'或直接尝试发一次HEAD请求 - 响应状态不是
206就别硬续传——可能服务端禁用了,此时应删掉已有文件重新下
requests 怎么发正确的 Range 请求
requests 本身不阻止你发 Range,但容易漏掉关键细节:header 值格式必须严格为 'bytes=12345-' (末尾短横不能少),且不能和 If-Range 冲突;另外,stream=True 必须开,否则响应体被提前读空,iter_content() 拿不到流。
使用场景:大文件下载中途被中断(网络抖动、程序崩溃、用户 Ctrl+C)后恢复。
立即学习“Python免费学习笔记(深入)”;
- 构造 header:
headers = {'Range': f'bytes={start}-'},start是本地文件当前字节数 - 必须加
stream=True:requests.get(url, headers=headers, stream=True) - 收到响应后立刻检查:
if response.status_code != 206,不是就报错退出,别往下走 - 写入时用
open(local_path, 'ab'),确保 append 模式,且不 truncate
怎么避免多线程/多进程下写坏文件
多个下载任务同时往同一个文件写,哪怕都用 'ab',也极大概率写乱——操作系统层面的 write() 不是原子操作,尤其在不同进程间共享 fd 时。断点续传默认是单任务串行行为,强行并发必须加锁或换方案。
性能影响:加文件锁会拖慢速度;用独立临时文件再合并,磁盘 IO 增加一倍;用内存 buffer 拼接再写入,内存占用不可控。
- 最稳做法:每个任务下载到独立临时文件(如
file.part001),最后用shutil.copyfileobj()按序拼接 - 若坚持单文件 + 多线程,必须用
os.open(..., os.O_APPEND | os.O_WRONLY)+os.write(),并配合flock(Linux/macOS)或msvcrt.locking()(Windows) - 别信“多个
open(..., 'ab')自动同步”——那是错觉,实测 3 线程下 10MB 文件错位率超 70%
校验续传结果是否完整可靠
只比对文件大小等于 Content-Length 不够——中间某段可能写失败但没报错,或者服务端返回了错误数据却没触发异常。真正可靠的验证只有两个:服务端提供 ETag 或 Content-MD5,或下载完成后本地算全量哈希。
容易踩的坑:边下边算 MD5 会误把未完成文件当完整体校验;用 HEAD 拿到的 Content-Length 可能和实际 206 响应里 Content-Range 的总长不一致(比如服务端压缩了)。
- 优先查响应头:
response.headers.get('Content-MD5')或response.headers.get('ETag'),有就解码后比对 - 没头信息就只能等下载完再算:
hashlib.sha256(open(local_path, 'rb').read()).hexdigest(),但大文件慎用.read(),改用分块更新 - 别省略
Content-Range解析:bytes 12345-67890/99999里的99999才是总长,不是HEAD返回的那个
断点续传最麻烦的从来不是“怎么发请求”,而是“怎么确认这一段真的写对了、没丢字节、没和其他段打架”。服务端配合度、本地文件系统行为、异常中断时机,三者叠加,任何一环松动都会让续传变成静默损坏。










