FastAPI 的 UploadFile 不支持断点续传,需用 Request 直接读取流并手动解析 Content-Range、管理 upload_id 状态、实现流式写入与偏移校验,同时配置 Nginx 关闭缓冲并延长超时。

FastAPI 中 UploadFile 默认不支持断点续传,必须自己处理 HTTP 头和分块逻辑
FastAPI 的 UploadFile 本质是读取整个 request.body 到内存或临时文件,它不解析 Content-Range、Upload-ID 等断点续传必需头,也不维护上传状态。想实现断点续传,得绕过 UploadFile,直接读取原始请求流,并手动解析分块信息。
典型错误是试图在依赖中写 file: UploadFile = File(...) 然后去读 file.file.seek() —— 这行不通,因为 FastAPI 已经消费完 body,且 UploadFile.file 是 SpooledTemporaryFile,不支持随机读写,更无法响应客户端的续传请求。
- 必须用
Request对象直接接收原始字节流 - 需校验并解析
Content-Range头(如bytes 1024-2047/5000000) - 需提取并校验客户端传来的唯一标识(如
X-Upload-ID或 URL path 中的upload_id) - 服务端要自行管理每个上传 ID 对应的临时文件路径、已写入偏移、总大小等元数据
用 Request + 文件系统实现流式写入与偏移校验
核心是把 request.stream() 当作异步迭代器,按 chunk 读取,再根据 Content-Range 定位写入位置。不要一次性 await request.body(),否则大文件会 OOM。
示例关键逻辑:
from fastapi import Request, HTTPException
import os
<p>async def handle_chunk_upload(request: Request):
upload_id = request.headers.get("X-Upload-ID")
content_range = request.headers.get("Content-Range")</p><pre class="brush:php;toolbar:false;">if not upload_id or not content_range:
raise HTTPException(400, "Missing X-Upload-ID or Content-Range")
# 解析 range: bytes 1024-2047/5000000 → (start, end, total)
try:
_, ranges = content_range.split(" ", 1)
byte_part, total_part = ranges.split("/")
start, end = map(int, byte_part.split("-"))
total_size = int(total_part)
except (ValueError, IndexError):
raise HTTPException(400, "Invalid Content-Range format")
file_path = f"/tmp/uploads/{upload_id}"
os.makedirs(os.path.dirname(file_path), exist_ok=True)
# 检查是否允许该偏移写入(防乱序、越界)
if start != os.path.getsize(file_path) if os.path.exists(file_path) else 0:
raise HTTPException(409, "Offset mismatch: client must resume from current size")
# 流式写入
async for chunk in request.stream():
with open(file_path, "ab") as f:
f.write(chunk)
注意:上面示例未加锁,高并发下多个分块同时写同一文件会出错。生产环境必须用文件锁(如 flock)或改用数据库记录每个分块的写入状态。
断点续传需要客户端和服务端约定一致的重试与状态查询接口
光能接收分块不够,客户端需要能随时问:“这个 upload_id 当前传到哪了?” 否则网络中断后无法知道从哪续。
- 提供
GET /upload/{upload_id}/status接口,返回{"offset": 123456, "total": 10485760} - 客户端首次上传时用
POST /upload/init获取新upload_id和预分配的total(可选) - 上传完成后再调用
POST /upload/{upload_id}/complete做校验(如 MD5)、重命名、触发后续处理 - 所有接口都必须校验
upload_id是否合法、是否过期(建议加 TTL,比如 24 小时自动清理)
常见坑:状态接口返回的 offset 直接用 os.path.getsize() —— 如果写入中途崩溃,文件可能残留脏数据,应额外维护一个元数据 JSON 文件(如 {upload_id}.meta)记录已确认写入的 offset。
大文件上传必须配 Nginx 反向代理超时与缓冲区调整
FastAPI 自身不设上传超时,但 Nginx 默认 60 秒超时、单次请求体上限 1MB,不调就必然失败。
在 Nginx 配置里至少加这几项:
location /upload/ {
client_max_body_size 2G;
client_body_timeout 3600;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
proxy_buffering off; # 关键:避免 Nginx 缓存整个 body 再转发
proxy_pass http://fastapi_backend;
}
如果漏掉 proxy_buffering off,Nginx 会等整个文件收完才发给 FastAPI,彻底失去流式意义;而 client_body_timeout 必须足够长,否则慢速上传(如移动网络)会被直接断连。
另外,若用 uvicorn 启动,别用 --limit-concurrency 过严,它会阻塞后续请求,导致状态查询接口也卡住 —— 断点续传依赖低延迟的状态反馈。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











