send_file大文件会全量加载内存,应改用response流式响应:用生成器分块yield、设mimetype和content-disposition头、必须启用conditional=true,并在nginx中关闭proxy_buffering。

因为 send_file 默认以完整路径调用时,会同步读取整个文件进内存再发给客户端——不是它设计错了,而是你没告诉它“别全读”。
send_file 路径模式默认走同步全量加载
当你写 send_file('/path/to/500MB.zip'),Flask 会调用 open(file_path, 'rb').read() 获取全部字节,再塞进响应体。这个过程:
- 没有流式控制,不支持 yield 或分块
- 内存峰值 ≈ 文件大小(加上 Python 对象开销)
- 工作线程被阻塞,无法处理其他请求
- 即使加了 as_attachment=True 或 conditional=True,也改不了这个底层行为
大文件必须用流式响应替代 send_file 调用
真正解决内存问题的不是调参,是绕过 send_file 的路径分支,直接构造流式响应:
- 用
Response包裹生成器函数,每次yield一个 chunk(如 8192 字节) - 手动设
mimetype和Content-Disposition头 - 确保
conditional=True——否则不支持断点续传、If-Range等 HTTP Range 请求 - 绝对路径仍需校验,但流式本身不依赖
safe_join,所以别混用send_from_directory和流式逻辑
示例:
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
def download_large():
path = '/var/data/report.pdf'
def stream():
with open(path, 'rb') as f:
while chunk := f.read(8192):
yield chunk
return Response(
stream(),
mimetype='application/pdf',
headers={'Content-Disposition': 'attachment; filename="report.pdf"'},
conditional=True
)
Nginx 反向代理会吃掉你的流式响应
本地测试流式响应正常,一上生产就卡死或变成一次性下载?大概率是 Nginx 在中间拦住了:
-
proxy_buffering on(默认开启):Nginx 会等后端整个响应结束才转发,yield彻底失效 - 必须在对应
location块里显式关掉:proxy_buffering off; - 顺带关掉缓存:
proxy_cache off;,避免 Nginx 缓存整个响应体 - 别忘了配
proxy_http_version 1.1;和proxy_set_header Connection '';,保证长连接透传
send_file 的流式参数其实是假流式
有人试过 send_file(io.BytesIO(data), ...),以为能省内存——其实只是把内存压力从磁盘 I/O 换成了 Python 对象构造:
-
io.BytesIO(data)依然要把整个data加载进内存才能初始化 - 真正的流式必须是惰性生成(generator)、按需读取(
f.read(n))、不预分配 -
send_file对BytesIO或StringIO的处理,底层仍可能触发.getvalue()全量提取 - 所以别用
BytesIO包大文件,直接用文件句柄 + 生成器更稳
最常被忽略的一点:流式响应的 conditional=True 不是可选项,是硬性要求。没有它,客户端无法做断点续传,浏览器刷新就会重头下,用户感知就是“卡住”或“重复下载”,而你以为是网络问题。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










