Python requests库如何POST流式上传大文件 降低内存占用
Python requests大文件低内存流式上传方案
问题根因
- 常规
files参数上传:requests会默认将整个文件加载到内存构造multipart/form-data格式请求体,2GB文件会直接占用等量内存,造成内存暴涨。 - 直接传
data=open(path,'rb'):虽然文件是逐块读取不会占满内存,但请求默认使用application/octet-stream作为Content-Type,不符合绝大多数文件上传接口要求的multipart表单格式,服务端无法解析到对应文件字段,因此返回400错误。
可行实现方案
方案1:使用requests-toolbelt实现(推荐,代码最简)
这个工具库是requests官方生态维护的组件,专门提供流式multipart编码能力,不会全量加载文件到内存。
首先安装依赖:
pip install requests-toolbelt
上传代码示例:
import requests import os from requests_toolbelt.multipart.encoder import MultipartEncoder # 构造流式multipart编码器,按固定块读取文件,内存仅缓存当前处理块 encoder = MultipartEncoder( fields={ # 键名'file'替换为你的接口实际要求的表单字段名,和原files参数的key保持一致 "file": (os.path.basename(path), open(path, "rb"), "application/octet-stream"), # 若接口需要额外传参(比如鉴权token、业务参数),直接在fields里追加键值对即可 # "upload_token": "your_auth_token" } ) response = requests.post( url=url, data=encoder, headers={"Content-Type": encoder.content_type}, # 建议设置超时:连接超时10秒,读取超时设为30分钟可根据带宽调整 timeout=(10, 1800) )
这个方案全程内存占用稳定在几十MB以内,和上传文件大小无关,2GB甚至更大的文件都不会造成内存压力。
方案2:原生requests无第三方依赖实现
如果不想额外安装依赖,可以自己实现multipart格式的流式生成器,逐块拼接请求体:
import requests import uuid import os def multipart_file_stream(file_path, field_name="file", chunk_size=10*1024*1024): """ 生成multipart/form-data格式的文件流,默认按10MB分块读取 :param field_name: 接口要求的文件表单字段名 :param chunk_size: 单次读取文件的块大小 """ # 生成随机boundary避免和文件内容冲突 boundary = f"----WebKitFormBoundary{uuid.uuid4().hex}" file_name = os.path.basename(file_path) # 写入表单头 yield ( f"--{boundary}\r\n" f"Content-Disposition: form-data; name=\"{field_name}\"; filename=\"{file_name}\"\r\n" f"Content-Type: application/octet-stream\r\n\r\n" ).encode("utf-8") # 逐块读取文件内容 with open(file_path, "rb") as f: while True: chunk = f.read(chunk_size) if not chunk: break yield chunk # 写入表单尾 yield f"\r\n--{boundary}--\r\n".encode("utf-8") # 生成上传流 upload_stream = multipart_file_stream(path) # 从生成器上下文取boundary构造请求头 boundary = upload_stream.gi_frame.f_locals["boundary"] headers = {"Content-Type": f"multipart/form-data; boundary={boundary}"} response = requests.post( url=url, data=upload_stream, headers=headers, timeout=(10, 1800) )
注意事项
- 不要在请求中传入
files参数,只要传入该参数,requests就会强制将所有内容加载到内存完成编码,流式处理会失效。 - 如果你对接的接口本身支持直接接收二进制流(不需要multipart格式),那
data=open(path,'rb')的写法本身是可用的,返回400时先和服务端确认要求的Content-Type和参数格式即可。 - 大文件上传建议显式设置timeout参数,避免网络异常时请求永久挂死。
内容的提问来源于stack exchange,提问作者Baytars
相关产品推荐
相关产品推荐

