StreamingResponse 是导出首选,因其支持边生成边发送、内存可控且避免超时;CSV 用 csv.writer 写入 StringIO 后流式返回,Excel 用 openpyxl 生成后分块 yield BytesIO 内容,并须手动设置正确的 Content-Type 和 Content-Disposition 响应头。

直接用 StreamingResponse 导出 CSV,用 StreamingResponse + openpyxl 或 xlsxwriter 导出 Excel 流——这是最稳妥、内存可控、浏览器能正确下载的组合。别用 FileResponse 返回临时生成的 Excel 文件,除非你已确保文件写完且路径绝对可靠;更别依赖 FastAPI 自动推断 media_type,一错就变乱码页。
为什么 StreamingResponse 是导出首选
CSV 和 Excel 导出本质是“边生成边发”,尤其数据量超过 1 万行时:FileResponse 要求文件已落盘,而写 Excel 文件(尤其是带样式)常需完整构建 workbook 后才能序列化,中间过程吃内存;StreamingResponse 允许你把生成逻辑封装成迭代器,让 FastAPI 分块传输,避免超时和 OOM。
- CSV 场景:用
csv.writer写入io.StringIO,再用iter([s.getvalue()])包一层即可流式返回 - Excel 场景:不能直接用
io.BytesIO().getvalue()一次性塞进iter,得用openpyxl的save到 BytesIO 后,再按 chunk 分片 yield(否则仍是内存全占) - 注意:不要对大 Excel 做
iter([wb_stream.getvalue()]),这等于没流式——getvalue()会把整个二进制加载进内存
Content-Type 和 Content-Disposition 必须手动设对
浏览器靠这两个响应头决定是否弹下载框、用什么程序打开。设错一个,Excel 就在页面里显示为乱码,或者被当成文本强行渲染。
- CSV:
media_type="text/csv",headers={"Content-Disposition": 'attachment; filename="report.csv"'} - Excel(.xlsx):
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",filename值必须用双引号包裹;含中文名时加filename*=UTF-8''report%E6%8A%A5%E8%A1%A8.xlsx(URL 编码) - 千万别用
application/octet-stream——它会让 Safari 拒绝识别为 Excel,Edge 可能截断文件名 - FastAPI 不会自动从文件扩展名推断 media_type,
response_class=StreamingResponse时必须显式传media_type
用 openpyxl 做流式 Excel 导出的实际写法
openpyxl 本身不原生支持流式写入,但你可以控制写入节奏:先创建 workbook → 写 header → 每写 1000 行就 save 到 BytesIO 并 yield 一部分,最后 flush 剩余内容。不过更轻量的做法是只用它生成最终二进制,再用 StreamingResponse 分 chunk 发送:
from openpyxl import Workbook
from io import BytesIO
from fastapi.responses import StreamingResponse
<p>def generate_excel_stream(data_rows):
wb = Workbook()
ws = wb.active
ws.append(["姓名", "年龄", "城市"])
for row in data_rows:
ws.append(row)</p><pre class="brush:python;toolbar:false;">stream = BytesIO()
wb.save(stream)
stream.seek(0)
# 分 8KB chunk 发送,避免单次 load 全部进内存
def iter_stream():
while True:
chunk = stream.read(8192)
if not chunk:
break
yield chunk
return StreamingResponse(
iter_stream(),
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
headers={"Content-Disposition": 'attachment; filename="report.xlsx"'}
)
- 关键点:yield 是在函数内部分块读取
BytesIO,不是把整个stream.getvalue()丢给iter - 如果数据源本身是数据库游标或生成器,务必用
fetchmany(1000)分批读,别fetchall() - 想加样式?
openpyxl支持,但样式越多,内存占用越不可控;超 10 万行建议放弃复杂样式,或切到xlsxwriter(它支持更高效的流式写)
大文件导出别卡在同步请求里
用户点下载按钮后等 3 分钟,Nginx 默认超时是 60 秒,前端早断连了。真实项目里,>5 万行的数据导出应该走异步任务:
- 接口立即返回
{"task_id": "DL20261002_abc123"} - 后台用 Celery 或
asyncio.create_task启动导出,写文件到共享存储(如 S3 或本地挂载目录) - 提供
GET /download/status/{task_id}让前端轮询,状态变为"ready"后返回预签名 URL 或重定向到FileResponse接口 - 临时文件必须带时间戳或 UUID 命名,且设置自动清理(比如 1 小时后删),不然磁盘迟早爆
流式导出看着简单,但真正压测时容易暴露内存泄漏、编码错误、响应头缺失这些细节问题——它们不会报错,只会让用户看到一片空白或乱码表格。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











