requests.post()无法断点续传,因http不维护上传状态,每次请求均为全新;需客户端分块发送+告知偏移量,服务端支持追加写入(如content-range或自定义offset参数),并协同校验位置一致性。

为什么直接用 requests 的默认 post 无法断点续传
因为 HTTP 协议本身不维护上传状态,requests.post() 每次都是全新请求,服务端无法知道“这是一半的文件”。断点续传必须由客户端和服务端协同完成:客户端要能分块发送 + 告知偏移量,服务端要能接收并追加写入。常见错误是只在客户端做“分片”,却没和服务端约定如何标识和合并这些分片,结果上传多次后文件内容错乱或覆盖。
- 服务端必须支持类似
Content-Range头或自定义参数(如offset=10240)来接收指定位置的数据 - 客户端需记录已上传字节数,失败后从该位置继续读取文件,不能重置文件指针
- 单纯用
seek()跳过已传部分还不够——还要确保服务端真的写到了对应位置,否则会覆盖或错位
如何用 requests 发送带偏移量的分块请求
核心是控制文件读取 + 构造正确请求头。不要一次性读整个文件,而是用二进制模式打开,按固定大小(如 8192 字节)循环读取,并用 seek() 定位起始位置。关键不是“分片”,而是每次请求明确告诉服务端:“这段数据应写入文件的第 X 字节开始”。
- 使用
with open(path, "rb") as f:打开文件,避免编码问题 - 调用
f.seek(offset)跳转到上次中断处,再用f.read(chunk_size)读取 - 在请求中加入自定义头,如
{"X-Offset": str(offset), "X-Total-Length": str(total_size)};若服务端支持标准Content-Range,则用"Content-Range": f"bytes {offset}-{end}/{total_size}" - 响应状态码必须检查:
200表示成功,206(Partial Content)常用于分块上传确认,409或416可能表示偏移冲突或越界
如何安全持久化已上传进度(避免重复上传或丢失 offset)
进度不能只存在内存里。网络中断、程序崩溃后,下次启动必须能准确读回上一次的 offset。最简方案是用一个同名的 .progress 文件存纯数字,但要注意写入原子性和并发风险。
- 写入时先写临时文件(如
file.txt.progress.tmp),再os.replace()原子替换,防止读到半截数据 - 读取前检查该文件是否存在且内容为有效整数,否则从 0 开始
- 不要把进度存在数据库或远程服务里——这会引入新依赖和单点故障,违背“本地可恢复”原则
- 上传完成后务必删除进度文件,否则下次会误以为要续传
服务端未提供断点接口时,能否硬套?
不能。如果服务端只是个普通 REST API(比如只接受 multipart/form-data 的单次 POST),强行在客户端分片只会导致服务端每次收到一个“新文件”,最后得到 N 个碎片而非一个完整文件。这时候要么换服务端协议(如改用 tus 协议),要么退回到全量重传 + 校验机制(如上传前计算 sha256,失败后比对服务端已有文件哈希)。
- tus 协议是事实标准,Python 有
tuspy库可直接对接支持 tus 的服务端(如 tusd) - 若只能用原生 HTTP,必须和服务端开发方确认是否支持
PUT+Content-Range,或是否预留了offset参数 - 别试图用
Range请求头去“下载式续传”上传——那是针对 GET 的,对 POST 无效
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











