requests直传大文件失败因无断点续传能力,需客户端分片上传、记录offset、服务端配合校验合并;须用seek/read切片、持久化upload_id与已传part状态、控制并发数、校验响应及etag、严格按序合并。

为什么 requests 直传大文件会失败
因为默认 HTTP 连接没有断点续传能力,上传中途断开就全得重来;requests 本身也不维护分片状态或校验逻辑。你看到的 ConnectionError、Timeout 或服务端返回 408 Request Timeout,基本都是这个原因。
真正能做断点续传的,是服务端配合客户端按块上传 + 校验 + 合并。Python 侧要做的,是把文件切片、记录已传 offset、失败时跳过已传块。
- 别用
requests.post(..., data=open(...))直传几 GB 文件——它会一次性读进内存,还无法中断恢复 - 必须自己控制分片读取:用
open(..., 'rb')+.seek()+.read(chunk_size) - 服务端需提供「查询已传分片」接口(比如
GET /upload/status?upload_id=xxx),否则客户端根本不知道该从哪继续
upload_id 和分片序号怎么生成和管理
这不是 Python 自动给的,得和服务端约定规则。常见做法是客户端首次请求获取一个全局唯一 upload_id(比如 UUIDv4),后续所有分片都带上它;每个分片带 part_number(从 1 开始整数)和 content_md5(可选但推荐)。
本地状态不能只靠内存存——进程重启就丢。最轻量做法是写个 JSON 文件记录:upload_state_{upload_id}.json,内容类似:
一款很稀有的jQuery+echarts上传图片生成文字标签云代码,基于echarts图表插件和jQuery实现根据图片样式生成彩色文字标签云特效,点击标签关键词还可以放大。
立即学习“Python免费学习笔记(深入)”;
{"upload_id": "a1b2c3", "file_path": "/tmp/big.zip", "chunk_size": 5242880, "uploaded_parts": [1, 2, 4], "total_parts": 12}
-
upload_id建议用uuid.uuid4().hex,避免时间戳+pid 等易冲突组合 - 分片序号必须严格连续且从 1 开始,有些对象存储(如腾讯云 COS)要求
part_number是正整数,传 0 或字符串会报InvalidArgument - 不要在每次上传前重新计算全部 MD5——只对当前 chunk 计算,存进状态文件里,用于失败重试时快速比对
并发上传分片反而更慢甚至被限流
不是线程越多越快。HTTP 连接复用、服务端并发阈值、本地 socket 资源都会卡住。实测多数云厂商对单 upload_id 的并发分片数限制在 5–10 之间,超了可能返回 429 Too Many Requests 或直接断连。
- 用
concurrent.futures.ThreadPoolExecutor(max_workers=3)比max_workers=20更稳 - 每个分片上传后必须检查响应状态码——
200或201才算成功,200但 body 里含"code": "PartAlreadyExists"也要视为成功(说明服务端已存) - 加指数退避重试:第一次失败等 1s,第二次 2s,第三次 4s,最多 3 次;别用固定 sleep,也别无脑 retry
合并分片失败的三个典型原因
前面传完 99 个分片,最后 POST /complete 报错,大概率不是网络问题,而是参数不匹配。
- 服务端要求
part_number必须按升序排列,传[{"part_number":3,"etag":"..."},{"part_number":1,"etag":"..."}]会直接拒掉 -
ETag不是简单md5(chunk),很多对象存储返回的是 base64(md5(chunk)) 或 multipart 专属 hash,必须用上传响应头里的Etag字段原样回传 - 合并请求的
Content-Type错了,比如该发application/json却发了text/plain,返回415 Unsupported Media Type
断点续传最难的不是传,是状态对齐——客户端认为“已传”,服务端没记上,或者反过来。上线前务必用人工 kill 进程 + 断网方式,真实模拟中断场景跑几轮。









