断点续传需手动实现:先HEAD/GET获取文件总大小,计算本地已下载字节作为Range起始偏移,发送"bytes=start-"请求;响应为206时用"ab"模式追加写入;通过Content-Range头校验是否完成,否则降级重下。

断点续传在 aiohttp 里不是开箱即用的功能,必须手动处理 Range 请求头、文件偏移写入和状态校验——否则下载中断后重试会从头开始或写入错位。
怎么发带 Range 的 GET 请求
aiohttp 默认不加 Range 头,你需要显式构造。关键点是:请求前先用 HEAD 获取文件总大小,再检查本地文件已存在多少字节,算出起始偏移。
- 用
session.head(url)拿Content-Length,注意有些服务器不支持 HEAD 或返回 405,得 fallback 到 GET +head=True参数 -
Range值格式必须是"bytes={start}-"(末尾短横不能省),比如已下 1024 字节,就传"bytes=1024-" - 服务器返回 206 Partial Content 才表示支持断点;如果返回 200,说明不支持或 Range 被忽略,此时应覆盖重下或报错
如何把响应流追加写入文件
不能直接用 await resp.text() 或 .read(),会把整个响应加载进内存,大文件必崩。必须边收边写,且要定位到文件末尾。
- 打开文件时用
"ab"模式(append binary),避免清空已有内容 - 用
async with aiofiles.open(path, "ab") as f:配合await f.write(chunk),确保异步写入不阻塞事件循环 - 注意:
os.stat(path).st_size是当前文件大小,就是下次请求的start偏移,别用f.tell()——它在"ab"模式下不可靠
怎么判断是否下载完成
不能只看响应体长度,得比对 Content-Range 头里的实际范围和总大小。
立即学习“Python免费学习笔记(深入)”;
- 从响应头取
resp.headers.get("Content-Range"),解析出类似"bytes 1024-9999/10000"中的10000 - 如果
end + 1 == total_size(注意 end 是闭区间),才算这次请求收完了最后一块 - 若没收到
Content-Range头,或解析失败,按不支持断点处理,建议中止或降级为全量下载
最易被忽略的是服务器对 Range 的实际响应行为:Nginx 默认支持,但 Caddy、某些 CDN 或自建服务可能静默忽略,返回 200 + 全量内容却不带 Content-Range——这时候你追加写入就会得到损坏文件。


















