分片下载需动态计算range:起始为i×chunk_size,结束为min((i+1)×chunk_size−1, content-length−1),用bytes={start}-{end};多线程须各写独立临时文件,合并时用shutil.copyfileobj流式追加写入。

requests 分片下载时 Range 头怎么设才不越界
分片下载失败,十有八九是 Range 值算错了——特别是最后一个分片,容易设成超出文件总大小,触发 416 Range Not Satisfiable 错误。
关键不是“按固定大小切”,而是根据服务器返回的 Content-Length 动态算结尾字节位置:
- 先发 HEAD 请求拿到
Content-Length(比如1234567) - 设分片大小为
chunk_size = 1024 * 1024(1MB),则第i片起始为i * chunk_size - 结束位置取
min((i + 1) * chunk_size - 1, content_length - 1),注意减 1(Range 是闭区间) - 请求头必须带
headers={'Range': f'bytes={start}-{end}'},不能漏bytes=
多线程写入同一个文件会损坏数据吗
会。直接用多个线程对同一文件句柄调用 write(),不加锁、不预分配,结果就是字节错乱、内容覆盖——哪怕你算好了每个线程该写哪段。
安全做法只有一条:每个线程写自己的临时文件,合并阶段再顺序拼接。
立即学习“Python免费学习笔记(深入)”;
- 分片下载时,给每片生成唯一临时名,如
f"part_{i:04d}.tmp" - 下载完全部分片后,按序读取这些
.tmp文件,用open(..., 'ab')追加写入目标文件 - 别用内存拼接大文件(比如
b''.join(chunks)),内存爆掉比下载慢更致命
合并时用 shutil.copyfileobj 还是 open().read()
用 shutil.copyfileobj。它底层用小缓冲块(默认 64KB)流式读写,内存占用稳定;而 open().read() 会把整个分片一次性加载进内存,100MB 分片就占 100MB 内存,N 片并发下载时极易 OOM。
将产品展示、购物管理、资金管理等功能相结合,并提供了简易的操作、丰富的功能和完善的权限管理,为用户提供了一个低成本、高效率的网上商城建设方案包含PowerEasy CMS普及版,主要功能模块:文章频道、下载频道、图片频道、留言频道、采集管理、商城模块、商城日常操作模块500个订单限制(超出限制后只能查看和删除,不能进行其他处理) 无订单处理权限分配功能(只有超级管理员才能处理订单)
实操示例(合并单个分片):
with open("part_0001.tmp", "rb") as fsrc:
with open("output.zip", "ab") as fdst:
shutil.copyfileobj(fsrc, fdst)-
shutil.copyfileobj默认缓冲区够用,不用改length参数 - 确保目标文件以
"ab"模式打开(追加二进制),不是"wb" - 如果分片数多,合并循环里别重复
open("output.zip", "ab"),应该在外层打开一次,传进去
断点续传怎么判断哪些分片已下载完成
不能靠文件存在就认为下载完成——可能写了一半就中断了。得校验实际字节数是否匹配预期。
每个分片下载前,先检查对应临时文件:
- 若文件不存在 → 正常下载
- 若存在但
os.path.getsize(path) != expected_size→ 删除重下(expected_size = end - start + 1) - 若大小匹配 → 跳过,直接进入合并流程
- 别用
os.path.exists()单独判断,那是坑
临时文件名建议包含起始偏移和长度(如 part_0_1048575.tmp),方便调试时一眼看出范围。
最麻烦的其实是网络波动导致部分分片反复失败,这时候重试逻辑要配超时和指数退避,而不是死循环重试。









