分块上传时chunk_size应设为8KB–64KB以避免OOM,过小增大HTTP开销,过大失去流式意义;需配合rb模式读取、显式Content-Length、禁用压缩及连接复用确保真正流式传输。

分块上传时如何控制 chunk_size 避免 OOM
直接读完整文件再上传,内存峰值等于文件大小;分块流式上传的核心就是让内存占用趋近于 chunk_size。但 chunk_size 并非越大越好——太小会放大 HTTP 开销和连接建立次数,太大则失去流式意义,尤其在低内存环境(如容器或边缘设备)下容易触发 MemoryError。
实操建议:
-
chunk_size推荐设为8192(8KB)到65536(64KB)之间;多数 HTTP 客户端(如requests)对这个范围有良好缓冲优化 - 避免用
os.path.getsize()后除以 chunk_size 算总块数——文件可能被并发修改,且不必要 - 不要用
file.read()一次性加载,改用file.read(chunk_size)迭代读取 - 上传大文件时,建议配合
requests.Session()复用连接,减少 TLS 握手开销
requests.post 如何真正实现流式 body 而不缓存
requests.post(url, data=file_obj) 表面是流式,但若传入的是普通 open() 文件对象,默认仍可能被 requests 内部转成 bytes 缓存——尤其当未显式指定 headers["Content-Length"] 或启用了某些适配器时。
确保真正流式的关键点:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 打开文件必须用
mode="rb",不能是文本模式 - 显式设置
headers={"Content-Length": str(os.path.getsize(filepath))},否则requests可能降级为Transfer-Encoding: chunked,部分服务端(如某些 MinIO 配置、Nginx 默认)会拒绝或静默截断 - 禁用自动压缩:传入
requests.post(..., headers={"Accept-Encoding": "identity"}),防止 requests 暗中解压/重编码 - 验证是否生效:用
tcpdump或mitmproxy观察请求体是否按预期分段发出,而非单次大包
boto3 的 upload_fileobj 为什么有时反而吃更多内存
用 boto3.client("s3").upload_fileobj(file_obj, bucket, key) 看似简洁,但它默认启用 multipart_upload,且内部会预读多个 chunk_size 块做缓冲——尤其当文件大于 8MB(boto3 默认分片阈值)时,内存占用可能飙升到几倍于单块大小。
更可控的做法:
- 手动调用
create_multipart_upload+upload_part+complete_multipart_upload,每块读完立即上传,上传后立刻del缓冲区 - 设置
Config(max_pool_connections=10, retries={"max_attempts": 2})控制并发与重试,避免连接池堆积待上传块 - 若确定文件小于 5MB,直接用
put_object(Body=file_obj)更轻量,且明确不走 multipart - 注意
upload_fileobj对io.BytesIO对象不友好——它会尝试.getbuffer(),导致整个内容驻留内存
上传中断后如何续传而不重头开始
纯流式上传本身不带断点续传能力;要支持恢复,必须把“已上传的字节偏移”持久化,并让服务端支持 Range 请求或分片 ID 映射。常见组合方案:
- S3 场景:记录每个
upload_id和已成功part_number列表,下次启动时调用list_parts获取已传分片,跳过重传 - 自建 HTTP 接口:要求服务端提供
HEAD /upload/{id}返回已接收字节数,客户端据此file.seek(already_uploaded)继续 - 本地状态存储别用临时文件——推荐写入 SQLite 或单行 JSON 文件,保证原子写入(如先写
.tmp再os.replace) - 切忌在多进程/多线程间共享同一份上传状态文件而不加锁;简单场景可用
flock,复杂场景建议用 Redis
最易被忽略的一点:流式上传的“内存友好”只在读取和传输链路干净时成立。一旦中间插入 base64 编码、gzip 压缩、或日志全量记录原始 chunk,内存占用立刻翻倍。动手前先确认每一层是否真的需要该处理。

















