无需预签名URL:Flask API传大文件至S3内存过高的解决办法
解决Flask API上传大文件到S3的内存占用过高问题
核心问题分析
Flask默认会把完整请求体加载到内存中,哪怕用upload_fileobj,如果没调整请求处理逻辑,文件数据还是会先存进内存,导致大文件上传时内存被占满。你的TransferConfig没起作用,本质是文件已经被Flask预加载到内存,boto3的分片配置无法改变这个前提。
解决方案步骤
1. 配置Flask禁用请求体预加载
修改Flask应用配置,避免自动把请求体读进内存,直接处理流式数据:
from flask import Flask, request app = Flask(__name__) # 不限制请求大小(按需调整为具体最大值) app.config['MAX_CONTENT_LENGTH'] = 0 # 避免异常时保留上下文,减少内存占用 app.config['PRESERVE_CONTEXT_ON_EXCEPTION'] = False
2. 直接流式传输请求体到S3
不要用Flask的request.files(它会把文件存到内存或临时文件),直接读取request.stream传给boto3,同时配合优化后的TransferConfig:
import boto3 from boto3.s3.transfer import TransferConfig s3_client = boto3.client('s3') @app.route('/upload', methods=['POST', 'PUT']) def upload_to_s3(): bucket_name = '你的存储桶名称' # 从请求头或参数获取对象键,按需调整 object_key = request.headers.get('X-Object-Key') transfer_config = TransferConfig( multipart_threshold=100*1024*1024, # 100MB以上启用分片 multipart_chunksize=25*1024*1024, # 每个分片25MB use_threads=True, max_concurrency=5, # 降低并发数,减少内存开销 io_chunksize=1*1024*1024 # 每次读取1MB数据 ) try: # 直接将请求流传给S3,不缓存到内存 s3_client.upload_fileobj( Fileobj=request.stream, Bucket=bucket_name, Key=object_key, Config=transfer_config ) return {'status': 'success'}, 200 except Exception as e: return {'error': str(e)}, 500
3. 额外优化建议
- 要求客户端发送正确的
Content-Length请求头,避免S3上传时出现异常;若使用分块传输编码,boto3也能兼容,但需确保请求格式正确。 - 不要将文件保存到本地临时目录,保持全程流式传输是最优方案。
- 调整Kubernetes Pod的内存请求和限制作为兜底,但优先通过代码优化减少内存占用。
为什么之前的TransferConfig没效果?
因为Flask已经把整个文件读进内存,upload_fileobj读取的是内存中的文件对象,而非流式的请求体,所以分片配置无法发挥作用。只有当Fileobj是真正的流式对象(比如request.stream)时,boto3才会按配置的块大小读取数据,不会一次性加载全部内容到内存。
内容的提问来源于stack exchange,提问作者thelostsoul
相关产品推荐
相关产品推荐

