18. FastAPI 响应类型_文件格式
文件响应与 JSON 响应的区别
下载接口发送文件字节,而不是把文件内容包装成 JSON。FileResponse 适合已有磁盘文件;StreamingResponse 适合运行时逐块生成的数据。二者都应明确文件类型和下载文件名。
使用 ID 映射文件,避免外部路径直通磁盘
from pathlib import Path
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse
app = FastAPI()
BASE = Path(__file__).resolve().parent / "downloads"
BASE.mkdir(exist_ok=True)
(BASE / "guide.txt").write_text("你好,文件下载!\n", encoding="utf-8")
FILES = {"guide": {"path": BASE / "guide.txt", "name": "学习指南.txt"}}
@app.get("/files/{file_id}")
def download(file_id: str):
meta = FILES.get(file_id)
if meta is None or not meta["path"].is_file():
raise HTTPException(404, "文件不存在")
return FileResponse(path=meta["path"], filename=meta["name"],
media_type="text/plain; charset=utf-8")保存为 main.py 并启动。访问 /files/guide 应下载文本文件;/files/unknown 返回 404。演示为便于复现会在启动时写入示例文件,实际业务应由上传或生成流程创建文件。
下载行为由什么决定
Content-Type 表示内容种类;Content-Disposition 表示附件下载或浏览器内联展示;filename 决定建议保存名称。FileResponse 能设置文件相关响应头,具体 Range 支持和缓存行为应以安装的 Starlette 版本为准。
使用 curl.exe -D headers.txt -o guide.txt http://127.0.0.1:8000/files/guide 可分别检查响应头和下载结果。PDF 常用 application/pdf;未知二进制常用 application/octet-stream。
临时生成文件的生命周期
不要在 with TemporaryDirectory() 退出后返回里面的文件路径,因为真正发送时文件可能已经删除。需要把清理安排到文件发送完成之后,例如通过响应的后台清理任务,或使用定期清理的缓存目录。
生成 CSV 时要正确处理逗号、换行、引号和编码,可使用标准库 csv,不要手工拼接分隔符。若供电子表格打开,还需根据业务处理以公式字符开头的不可信单元格内容。
进一步的工程处理
文件 ID 不等于访问授权;先检查当前用户能否下载,再返回文件。不要接受 ?path=C:/... 直接读取服务器文件。对象存储可使用短时授权链接,海量下载可交给专门的文件服务。受保护文件的缓存策略应避免共享缓存泄露。
练习:增加 PDF 文件映射;测试不存在的 ID 和实际文件被删除两种情况。比较浏览器下载和 curl.exe -i 的响应头。