游乐游手机版
首页/业界动态/文章详情

FastAPI 文件上传/下载的三个坑,你踩过几个?

时间:2026-04-21 15:12
你以为 FastAPI 封装得够好了,随便写两句就能跑?天真 做后端开发,文件上传和下载是绕不过去的坎。FastAPI 的封装确实优雅,但如果你以为随便写两句就能高枕无忧,那可就太天真了。今天,我们就来盘点三个在实际项目中高频踩坑的场景,看看你中招了几条。 坑一:大文件上传,内存原地爆炸 很多新手第

你以为 FastAPI 封装得够好了,随便写两句就能跑?天真

做后端开发,文件上传和下载是绕不过去的坎。FastAPI 的封装确实优雅,但如果你以为随便写两句就能高枕无忧,那可就太天真了。今天,我们就来盘点三个在实际项目中高频踩坑的场景,看看你中招了几条。

坑一:大文件上传,内存原地爆炸

很多新手第一次写文件上传接口,大概率会这么写:

from fastapi import FastAPI, UploadFile
import shutil

app = FastAPI()

@app.post("/upload")
async def upload_file(file: UploadFile):
    contents = await file.read()
    with open(f"./uploads/{file.filename}", "wb") as f:
        f.write(contents)
    return {"filename": file.filename}

乍一看没毛病,用 Postman 一测,小文件秒传,信心瞬间爆棚。然而,当产品经理扔过来一个 500MB 的视频文件时,接口直接 OOM(内存溢出),进程当场挂掉。

问题出在哪里?关键就在 await file.read() 这一行。它一次性把整个文件流都读进了内存,这谁顶得住?

正确的做法是采用流式写入,让内存压力降到最低:

@app.post("/upload")
async def upload_file(file: UploadFile):
    sa ve_path = f"./uploads/{file.filename}"
    with open(sa ve_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
    return {"filename": file.filename}

shutil.copyfileobj 会自己管理缓冲区,将文件数据一块一块地写入磁盘,内存占用可以稳定控制在几 MB 以内。说到底,这个坑的教训就是:别贪图方便,试图一口吞下整个文件流。

坑二:文件下载,中文文件名 URL 乱码

后端接口写得漂漂亮亮,前端调用也一切顺利,结果用户下载下来的文件名变成了一串 %E4%B8%AD%E6%96%87 这样的“天书”,用户体验直接归零。

这其实不算是你的 Bug,而是 HTTP 协议中 URL 编码规则导致的。根据 RFC 5987 标准,如果文件名包含非 ASCII 字符(比如中文),就必须使用 encodeURIComponent 或者 RFC 5987 规定的 filename* 语法来处理。

最稳妥的做法,是在响应头里同时提供两个选项:

from fastapi import Response
import os
from urllib.parse import quote

@app.get("/download/{filename}")
async def download_file(filename: str, response: Response):
    file_path = f"./uploads/{filename}"
    if not os.path.exists(file_path):
        return {"error": "文件不存在"}

    # 兼容中文文件名
    safe_name = os.path.basename(filename)
    response.headers["Content-Disposition"] = (
        f"attachment; filename={safe_name}; "
        f"filename*=UTF-8''{quote(safe_name)}"
    )

    return Response(
        open(file_path, "rb").read(),
        media_type="application/octet-stream",
        headers=response.headers
    )

简单解释一下:filename 供现代浏览器使用,而 filename*=UTF-8'' 则为老式浏览器提供兜底方案。两者并存时,浏览器会自动选择它能理解的那个。另外,别忘了导入 from urllib.parse import quote

坑三:文件类型不校验,后端成了木马上传入口

很多教程讲到文件上传就戛然而止,对文件类型校验只字不提。这在内部系统中或许还能接受,但对于任何有基本安全要求的系统,这无疑是一个高危漏洞。

想象一下,如果你的接口允许上传 .py 文件,攻击者完全可以上传一个能执行系统命令的脚本,然后直接访问它——这跟 SSRF(服务器端请求伪造)漏洞的危害不相上下。

最基础的防护手段,是同时校验文件扩展名和 MIME 类型:

ALLOWED_EXTENSIONS = {"jpg", "jpeg", "png", "pdf", "docx"}
ALLOWED_MIME_TYPES = {"image/jpeg", "image/png", "application/pdf", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}

@app.post("/upload")
async def upload_file(file: UploadFile):
    ext = file.filename.rsplit(".", 1)[-1].lower() if "." in file.filename else ""
    if ext not in ALLOWED_EXTENSIONS:
        return {"error": f"不支持的文件类型: .{ext}"}

    if file.content_type not in ALLOWED_MIME_TYPES:
        return {"error": f"不支持的MIME类型: {file.content_type}"}

    sa ve_path = f"./uploads/{file.filename}"
    with open(sa ve_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
    return {"filename": file.filename}

当然,如果对安全性有更高要求,更佳实践是将文件直接存储到对象存储(如 OSS / S3)中,后端只返回一个具有时效性的临时访问链接,彻底杜绝用户直接访问服务器文件路径的可能性。这又是另一个话题了,有机会可以单独展开聊聊。

总结

三个高频坑点讲完了。回过头看,其实都是非常基础的问题:处理大文件要流式操作、中文文件名需规范编码、上传文件必须校验类型。说难吗?并不复杂。说简单吗?却总有人栽跟头。关键就在于,写代码时脑子里得时刻绷紧这根弦。把这些细节做到位,你的服务才能既稳健又安全。

来源:https://www.51cto.com/article/841027.html
上一篇2026 大中型企业企微 SCRM 横评:4 款主流产品实测,谁能扛起集团级私域大旗? 下一篇低停机!一文搞定MySQL生产环境平滑升级
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
长安汽车明年一季度发布首款车载人形机器人小安
业界动态 · 2026-06-29

长安汽车明年一季度发布首款车载人形机器人小安

长安汽车公布机器人战略,采用“1+N+X”布局,联合头部伙伴攻克大脑、能源、驱动技术。人形机器人“小安”身高169cm,体重69kg,移动速度0 8m s,具备40个自由度,续航超2小时。预计明年一季度发布首款车载组件机器人,已在广州车展展示。

中国信科刷新光通信世界纪录 每秒可下载1.4万部4K电影
业界动态 · 2026-06-29

中国信科刷新光通信世界纪录 每秒可下载1.4万部4K电影

3月25日,光通信领域迎来又一个里程碑:中国信科集团光通信技术和网络全国重点实验室联合鹏城实验室、烽火藤仓光纤科技有限公司,成功实现了2 5Pb s 24芯光纤超大容量实时光传输,再次刷新了世界纪录。 这一研究成果不仅入选国际顶级光通信会议OFC(2026)并荣获“高分论文”称号,还受国际权威SCI

美国调查18万辆特斯拉Model3车门应急释放装置易找性
业界动态 · 2026-06-29

美国调查18万辆特斯拉Model3车门应急释放装置易找性

美国国家公路交通安全管理局对约17 9万辆2024款特斯拉Model3启动缺陷调查,焦点在于车门应急释放装置是否不易找到且标识不清。该调查源于一份缺陷请愿,不意味着立即召回,但可能引发后续监管措施。

doc个人图书馆停服 创始人称无偿转让失败
业界动态 · 2026-06-29

doc个人图书馆停服 创始人称无偿转让失败

运营长达20年,累计服务8000万用户的360doc个人图书馆,最终还是迎来了谢幕时刻。2026年5月1日,这个承载着无数用户收藏记忆的知名平台将正式停止服务——关停原因并非用户流失,而是始终未能寻得一位能够安全接管的合适人选。 创始人蔡智在告别信中坦言,近两个月来,他一直在尝试将360doc无偿转

年Q1随身WiFi实测安全靠谱高性价比机型推荐
业界动态 · 2026-06-29

年Q1随身WiFi实测安全靠谱高性价比机型推荐

2025年10月,艾瑞咨询正式授予飞猫“AI WiFi品类开创者”认证,紧接着CIC也将其认定为“多网融合自由切换技术服务首创者”。这些权威认证背后,折射出一个清晰的市场趋势:移动办公、户外出行、宿舍上网等场景的需求正在快速增长,随身WiFi几乎已成为不少用户的刚需装备。但问题也随之而来——网络卡顿