断点续传核心是准确记录并恢复文件偏移量,依赖临时文件或目标文件大小获取已下载字节数,HTTP需Range头与206响应,本地传输靠seek()定位,写入须用追加模式并强制fsync,完成后须校验哈希且原子替换文件。

断点续传的核心是记录和恢复文件偏移量
关键不在“断”,而在“续”——必须让程序知道上次传到哪了。常见做法是用一个临时文件(如 filename.part)保存已写入的字节数,或直接读取目标文件当前大小作为续传起点。HTTP 协议靠 Range 请求头实现服务端分片,而本地文件传输则依赖 seek() 定位写入位置。
requests + Range 头实现 HTTP 断点续传
服务端需支持 Accept-Ranges: bytes,否则 Range 请求会返回 200 而非 206。实操时先检查目标文件是否存在,再用 os.path.getsize() 获取已下载字节数,构造请求头:
headers = {"Range": f"bytes={downloaded_size}-"}
注意:Range 值末尾带短横线表示“从该位置到结尾”,不要写成 f"bytes={downloaded_size}-{total_size-1}"(你未必知道 total_size)。
- 收到 206 响应才可续传;若返回 200,说明服务端不支持,得重下
- 写入文件前必须用
open(..., "ab")模式,不能用"wb"覆盖 - 务必校验
Content-Range响应头中的起始偏移是否匹配预期
本地文件复制的断点续传更简单但易错
本质是 seek() + read() + write() 的组合。难点在于:源文件可能被修改、目标路径权限不足、磁盘空间突然不足。建议步骤:
立即学习“Python免费学习笔记(深入)”;
- 用
os.stat(src).st_size获取源文件总大小 - 用
os.path.getsize(dst)获取目标已写大小,确认未超源大小 - 打开目标文件用
"r+b"模式,fp.seek(downloaded_size)定位 - 循环
read(8192)写入,每次写完立即fp.flush()和os.fsync(fp.fileno())
漏掉 fsync 可能导致断电后最后几 KB 丢失,且不会报错。
别忽略校验和与原子性
断点续传完成后,md5 或 sha256 校验不是可选项。更关键的是:不要直接覆盖原文件。正确流程是下载到 file.tmp → 校验通过 → os.replace("file.tmp", "file")。Windows 下 replace 是原子操作,Linux 下也等效于 rename。用 shutil.move 或直接 os.rename 在某些 NFS 或容器挂载场景会失败。
真正麻烦的从来不是“怎么续”,而是“续完怎么信”。校验失败时,是删掉部分文件重来,还是尝试修复?这取决于业务容忍度,但代码里必须有明确分支处理,不能只打印一句“校验失败”。










