send_file最稳妥,自动处理MIME类型、Content-Length和缓存头;路径须为绝对或相对app.root_path;内存字节流需用io.BytesIO包装;大文件应流式响应,返回生成器yield分块bytes。
Flask怎么用
返回图片或PDF这类二进制文件
直接用
最稳妥,它自动处理 MIME 类型、Content-Length 和缓存头,比手动构造响应安全得多。别自己拼
+
,容易漏掉
或编码声明。
常见错误现象:
(误把字节当字符串)、浏览器下载乱码文件、图片显示为黑屏或“无法加载”。
路径必须是服务器本地存在的绝对路径或相对于
的相对路径;传入内存字节流时,得用
包一层再传给
如果文件来自数据库 blob 字段或网络请求结果,别写成
—— 临时文件有竞态和清理风险,直接走
+
参数
参数不填也能猜,但建议显式指定,比如
;PDF 必须设
,否则 Chrome 可能强行在线预览失败
大文件(>10MB)慎用
默认行为,它会一次性读入内存;改用
(需 Nginx 配合)或分块流式响应(见下一条)
需要流式传输大文件时,为什么不能直接 return
对象
Flask 的响应体要求是可迭代的字节序列(
),而
本身不是可迭代对象 —— 它得先
(吃内存)或用
分块调用才行。
使用场景:导出百万行 CSV、返回视频片段、代理下游服务的原始响应流。
立即学习
“
Python免费学习笔记(深入)
”;
Python 3.14.3
微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。
下载
正确做法是返回一个生成器函数,每次 yield 一段
,例如
;Flask 会边生成边发包
记得在
初始化时设
和
,尤其
对某些客户端有用
别在生成器里做耗时操作(如逐行查数据库),会阻塞整个响应流;提前准备好数据管道,或用后台任务+下载链接替代
WSGI 服务器(如 Gunicorn)默认缓冲响应,加
可减少中间拷贝,但需确保生成器稳定不抛异常
+
手动构造响应的适用边界在哪
只在极少数情况需要:你要完全控制每个 header(比如自定义加密校验头)、响应体是固定小字节块(
参数差异明显:
中的
必须是
,传
会触发隐式 UTF-8 编码,导致图片损坏。
必须手动设置
,漏掉就变成
,浏览器当成网页解析字节流,必然出错
必须确保
是 raw bytes,比如从
来,而不是
出来的
没有自动
,小数据无所谓,但大文件没这个头,某些代理或 CDN 会截断响应
别和
混用——
只接受 Python 原生类型,传
直接报
前端拿到二进制流后,为什么
+
有时打不开图片
根本原因不是 Flask 端,而是前端没正确处理响应类型或 MIME 不匹配。Flask 发的是对的,但 JS 拿错了数据形态。
常见错误现象:Blob URL 打开空白页、控制台报 “Failed to execute 'createObjectURL' on 'URL'”,或者图片显示为损坏图标。
fetch 时必须加
或干脆不设
,避免服务器因内容协商返回 HTML 错误页
response 的
方法依赖
响应头,如果 Flask 返回时没设对(比如设成
),Blob 构造后仍可能被浏览器拒绝渲染
用
后,记得在不用时调用
,否则内存泄漏
调试时先 curl 看原始响应头:
,确认
和
是否合理
实际用的时候,90% 的图像/文件接口走
就够了;剩下 10% 流式场景,重点不在怎么写生成器,而在控制好数据源节奏和错误兜底——流一旦开始,中途崩了前端就卡死,没法重试。
send_filesend_fileResponsebytesContent-TypeUnicodeDecodeErrorapp.root_pathio.BytesIOsend_filesend_file("/tmp/xxx")io.BytesIO(data)download_namemimetypemimetype="image/png""application/pdf"send_fileuse_x_sendfile=TrueBytesIOIterable[bytes]BytesIO.getvalue().read()bytesyield chunkResponsecontent_typeheaders"Content-Transfer-Encoding": "binary"Response(..., direct_passthrough=True)make_responsebytesmake_response(data)databytesstrresponse.headers["Content-Type"]text/htmldataopen("x.png", "rb").read()str.encode()Content-LengthjsonifyjsonifybytesTypeErrorfetchblob(){ headers: { "Accept": "image/*" } }Accept.blob()Content-Type"text/plain"URL.createObjectURL(await response.blob())URL.revokeObjectURL()curl -I http://localhost:5000/imgContent-TypeContent-Lengthsend_file