使用 FastAPI 实现文件上传接口:从入门到进阶

在现代 Web 应用中,文件上传 几乎是所有系统的标配功能:无论是头像上传、文档提交,还是批量导入数据。 本文将详细介绍如何使用 FastAPI 快速、高效地实现文件上传接口,并给出进阶场景(如多文件上传、限制文件大小、异步保存文件等)的最佳实践。


一、FastAPI 文件上传的核心机制

FastAPI 内置了强大的文件上传支持,基于 starletteUploadFile 对象实现。 它的底层使用 Python 的异步文件处理机制,性能优于传统的同步文件读取。

在 FastAPI 中,文件上传参数可以通过以下两种类型声明:

  • File(...):用于声明表单中的文件字段。
  • UploadFile:封装上传文件对象,包含 filenamecontent_typefile 等属性。

二、单文件上传示例

先看一个最简单的例子:上传一个文件并保存到服务器。

python 复制代码
from fastapi import FastAPI, File, UploadFile
import shutil
import os

app = FastAPI()

UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)

@app.post("/upload/")
async def upload_file(file: UploadFile = File(...)):
    file_path = os.path.join(UPLOAD_DIR, file.filename)
    
    # 保存文件
    with open(file_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
    
    return {"filename": file.filename, "content_type": file.content_type}

说明:

  • UploadFile 是一个异步文件对象(底层为 SpooledTemporaryFile)。
  • file.file 是标准的类文件对象,可直接读写。
  • shutil.copyfileobj() 用于高效复制二进制数据流。

访问接口:

bash 复制代码
curl -F "file=@example.png" http://127.0.0.1:8000/upload/

返回:

json 复制代码
{
  "filename": "example.png",
  "content_type": "image/png"
}

三、多文件上传

FastAPI 支持一次性上传多个文件,只需将类型改为 List[UploadFile]

python 复制代码
from typing import List
from fastapi import UploadFile, File

@app.post("/upload/multiple/")
async def upload_multiple(files: List[UploadFile] = File(...)):
    saved_files = []
    for file in files:
        file_path = os.path.join(UPLOAD_DIR, file.filename)
        with open(file_path, "wb") as buffer:
            shutil.copyfileobj(file.file, buffer)
        saved_files.append(file.filename)
    return {"uploaded_files": saved_files}

上传多个文件:

bash 复制代码
curl -F "files=@a.png" -F "files=@b.jpg" http://127.0.0.1:8000/upload/multiple/

四、异步写入与性能优化

上面的写入方式是同步的。如果文件较大,可能阻塞事件循环。 可以使用异步文件写入方式提升性能(推荐在高并发或大文件场景):

python 复制代码
import aiofiles

@app.post("/upload/async/")
async def upload_file_async(file: UploadFile = File(...)):
    file_path = os.path.join(UPLOAD_DIR, file.filename)
    async with aiofiles.open(file_path, 'wb') as out_file:
        while content := await file.read(1024):
            await out_file.write(content)
    await file.close()
    return {"filename": file.filename}

⚙️ 提示:aiofiles 是一个异步文件操作库,可与 FastAPI 无缝协作。

安装依赖:

bash 复制代码
pip install aiofiles

五、限制上传文件大小与类型

限制大小(Nginx / Uvicorn 层)

FastAPI 本身不限制文件大小。建议在 网关层(如 Nginx)限制上传大小:

nginx 复制代码
client_max_body_size 10M;

校验文件类型

你可以在接口中检查文件类型:

python 复制代码
ALLOWED_TYPES = ["image/png", "image/jpeg"]

@app.post("/upload/image/")
async def upload_image(file: UploadFile = File(...)):
    if file.content_type not in ALLOWED_TYPES:
        return {"error": "Unsupported file type"}
    # 继续保存逻辑...
    return {"filename": file.filename}

六、结合 Pydantic 校验表单 + 文件混合上传

在实际业务中,文件上传通常伴随表单数据(如用户ID、描述等)。 FastAPI 支持文件与表单字段混合处理:

python 复制代码
from fastapi import Form

@app.post("/upload/with_form/")
async def upload_with_form(
    user_id: int = Form(...),
    description: str = Form(""),
    file: UploadFile = File(...)
):
    file_path = os.path.join(UPLOAD_DIR, file.filename)
    with open(file_path, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
    return {"user_id": user_id, "description": description, "filename": file.filename}

七、实际应用场景建议

场景 推荐方案
上传头像、小文件 使用 UploadFile + 异步保存
批量导入Excel、CSV 使用 aiofiles 逐行读取,解析后入库
上传到云存储(如OSS、S3) 直接使用 boto3oss2 SDK,将文件流转存至云端
接口网关安全限制 使用 Nginx / Traefik 限制文件大小和MIME类型
文件命名冲突 使用 UUID 或时间戳重命名文件

八、总结

FastAPI 让文件上传的实现变得异常简单且高效。 通过本文的实践,你已经学会了:

✅ 基本文件上传与保存 ✅ 多文件与异步写入 ✅ 文件类型与大小校验 ✅ 表单与文件混合上传 ✅ 性能与安全最佳实践

接下来,你可以尝试:

  • 实现一个图片上传 + 自动压缩接口
  • 结合 JWT 用户认证,构建安全的文件上传系统
  • 将文件元数据存储在数据库中,实现可追踪的文件管理系统

✍️ 作者建议

在实际项目中,推荐将文件上传逻辑封装为独立的服务模块(如 services/file_service.py), 结合日志(如 loguru)和异常处理中间件,让系统更加可维护与可追踪。

相关推荐
止语Lab1 小时前
好的 DX 不等于少写代码——三种语言的摩擦力设计课
后端
吃饱了得干活1 小时前
别再手动解析 LLM 输出了!LangChain 四种结构化输出方案对比
后端·python·langchain
程序员天天困1 小时前
Arthas trace 命令怎么用?一行定位最慢那行代码
jvm·后端
Huiturn1 小时前
GPT 5.6 连续编码 10 小时,纯 Python 啃下 Word 二进制格式——doc2docx 实现拆解
后端
用户298698530141 小时前
Python 实现 Excel 与 Markdown 互转的实用指南
后端·python·excel
用户77283104908401 小时前
krono-job:零侵入、单二进制交付的分布式任务调度平台(开源)
后端
Conan在掘金1 小时前
鸿蒙报错速查:Function lacks ending return statement,返回路径漏 return 就炸,根因 + 真解法
后端
人间凡尔赛2 小时前
AI-Native 云原生架构:2026 年从容器编排到智能体编排的范式革命
后端·云原生·架构
IT_陈寒2 小时前
Vue的响应式让我加班到凌晨3点,原来问题出在这
前端·人工智能·后端
卷无止境2 小时前
提升 Python 代码健壮性的方法大盘点
后端·python