FastAPI流式传输大文件需用StreamingResponse配合自定义生成器分块读取,禁用FileResponse;上传大文件应绕过UploadFile改用Request.stream(),并配置Uvicorn、Nginx及Cloudflare的请求体大小限制。

FastAPI里用StreamingResponse传大文件不卡内存
直接返回大文件(比如 2GB 视频)时,别用 FileResponse——它会一次性读进内存,服务容易 OOM。必须走流式,核心是让 FastAPI 不缓冲、不预加载,边读边发。
关键点:自己控制读取 chunk 大小,配合 iter_file 或生成器;响应头要设对,否则浏览器可能不识别为可下载文件。
-
StreamingResponse的media_type建议显式设为"application/octet-stream",避免 FastAPI 自动猜错 MIME 导致乱码或无法下载 - 用
open(path, "rb")+iter+read构造生成器,每次只读8192字节(太小增加 syscall 开销,太大失去流控意义) - 务必加
headers={"Content-Disposition": 'attachment; filename="xxx.zip"'},否则 Safari 可能直接渲染二进制内容
from fastapi import Response
from starlette.responses import StreamingResponse
def iter_file(file_path: str):
with open(file_path, "rb") as f:
while chunk := f.read(8192):
yield chunk
@app.get("/download")
def download_file():
return StreamingResponse(
iter_file("/tmp/large.zip"),
media_type="application/octet-stream",
headers={"Content-Disposition": 'attachment; filename="large.zip"'}
)
上传大文件时禁用 FastAPI 默认的 form 解析
默认用 UploadFile 接收文件,底层依赖 form 解析器,会把整个上传体缓存到内存或临时文件——上传 500MB 文件时,可能触发 RequestEntityTooLarge 或耗尽磁盘空间。
真正可控的方式是绕过 form,直接读原始 request body 流,自己分块处理。
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 去掉所有
UploadFile参数,改用Request对象 - 用
request.stream()获取异步迭代器(注意:这是 Starlette 的stream(),不是 FastAPI 封装过的) - 别用
await request.body(),它会一次性加载全部内容 - 写入目标文件时用
asyncio.to_thread包裹open(..., "wb").write,避免阻塞事件循环
client_max_body_size 和 upload_max_size 不是 FastAPI 配置
很多人在 app = FastAPI(...) 里找上传大小限制参数,找不到是对的——FastAPI 本身不设这个。限制来自部署层:Uvicorn、Nginx 或 Cloudflare。
常见错误是只调大 Uvicorn 的 --limit-concurrency,却忘了它不控制单请求体大小。
- Uvicorn 要加
--limit-max-requests 0 --timeout-keep-alive 5,但真正管 body 大小的是--limit-request-body(单位字节),例如--limit-request-body 2147483648放开到 2GB - Nginx 必须配
client_max_body_size 2G;,且该指令需放在http、server或location块中,漏掉任一层都无效 - 若前端走 HTTPS 且经 Cloudflare,还要在 Cloudflare Dashboard 关闭
Web Application Firewall的“限制上传大小”规则(默认 100MB)
断点续传上传需要自己解析 Range 和 Content-Range
浏览器分片上传(如 axios 的 onUploadProgress)或网络中断重试,依赖 HTTP Range 协议。FastAPI 默认不支持,得手动处理请求头和状态码。
这不是加个装饰器就能搞定的事,涉及文件偏移定位、并发写入冲突、ETag 校验等细节。
- 检查请求头是否有
Range,格式类似bytes=0-1023;没有则按普通上传走 - 响应必须返回
206 Partial Content,并带Content-Range头,例如Content-Range: bytes 0-1023/2048 - 写文件前先
os.lseek(fd, offset, os.SEEK_SET)定位,避免覆盖已传部分(注意:Windows 上lseek对普通文件句柄有效,但某些异步文件系统需换用truncate+seek组合) - 别忽略
If-Match头——客户端可能带 ETag 要求校验,不校验会导致重复上传覆盖

















