FastAPI使用StreamingResponse返回大文件内存溢出,如何流式输出?
FastAPI大文件流式返回解决方案
你的问题核心在于当前代码里的file.read()会一次性将整个文件加载到内存,哪怕设置了buffering参数也没用——因为不带参数的read()默认读取文件全部内容。而StreamingResponse需要的是一个可迭代/异步可迭代对象来逐块输出数据,而非一次性的字节流。
下面提供两种可行的实现方式:
1. 同步流式返回(适合常规场景)
通过生成器函数逐块读取文件,避免全量加载:
from starlette.responses import StreamingResponse def file_generator(file_path, chunk_size=1024*1024): # 按1MB分块,可按需调整 with open(file_path, "rb") as f: while chunk := f.read(chunk_size): yield chunk # 接口中的返回逻辑 filename = "your_large_file.ext" headers = {'Content-Disposition': f'attachment; filename="{filename}"'} return StreamingResponse( content=file_generator(filename), media_type="application/octet-stream", headers=headers )
- 用
with语句自动管理文件句柄,避免资源泄漏; - 生成器每次返回一块数据,
StreamingResponse会逐块发送给客户端,内存仅保留当前块的大小。
2. 异步流式返回(与上传逻辑统一,适合高并发)
如果你的服务以异步为主,用aiofiles配合异步生成器实现全异步流式返回:
import aiofiles from starlette.responses import StreamingResponse async def async_file_generator(file_path, chunk_size=1024*1024): async with aiofiles.open(file_path, "rb") as f: while chunk := await f.read(chunk_size): yield chunk # 接口中的返回逻辑 filename = "your_large_file.ext" headers = {'Content-Disposition': f'attachment; filename="{filename}"'} return StreamingResponse( content=async_file_generator(filename), media_type="application/octet-stream", headers=headers )
- 全程异步IO,不会阻塞FastAPI的事件循环,更适合高并发场景;
- 同样逐块读取返回,彻底避免内存溢出问题。
额外注意事项
- 务必避免使用无参数的
file.read(),这是导致内存溢出的根本原因; Content-Disposition的格式要正确,文件名需用双引号包裹,避免浏览器解析异常;chunk_size可根据服务器内存和网络情况调整,常见取值为1MB~16MB。
内容的提问来源于stack exchange,提问作者Drowzee
相关产品推荐
相关产品推荐

