FastAPI 文件上传
FastAPI 提供了 File 和 UploadFile 两种方式来处理文件上传,支持单独上传文件和表单与文件混合上传。
安装依赖
文件上传同样依赖 python-multipart:
pip install python-multipart
使用 UploadFile
UploadFile 是推荐的方式,它提供了更丰富的文件信息访问接口:
实例
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
# UploadFile 提供的属性和方法
return {
"filename": file.filename, # 文件名
"content_type": file.content_type, # 文件 MIME 类型
"size": file.size, # 文件大小(字节)
}
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
# UploadFile 提供的属性和方法
return {
"filename": file.filename, # 文件名
"content_type": file.content_type, # 文件 MIME 类型
"size": file.size, # 文件大小(字节)
}
UploadFile 的属性和方法:
| 属性/方法 | 类型 | 说明 |
|---|---|---|
filename | str | None | 上传文件的原始文件名 |
content_type | str | None | 文件的 MIME 类型(如 image/png) |
file | SpooledTemporaryFile | 类似文件的对象,可读取文件内容 |
size | int | None | 文件大小(字节) |
read() | 方法 | 读取文件内容为 bytes |
write() | 方法 | 向文件写入内容 |
seek() | 方法 | 移动文件指针位置 |
close() | 方法 | 关闭文件 |
UploadFile使用"SpooledTemporaryFile"存储文件内容,文件较小时保存在内存中,文件较大时自动写入磁盘,因此比bytes更节省内存。
使用 File 接收文件内容
File 直接将文件内容读取为 bytes,适合小文件:
实例
from fastapi import FastAPI, File
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
# file 是文件的原始字节数据
return {"file_size": len(file)}
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
# file 是文件的原始字节数据
return {"file_size": len(file)}
File 与 UploadFile 的对比:
| 对比项 | File | UploadFile |
|---|---|---|
| 数据类型 | bytes | 文件对象 |
| 内存使用 | 整个文件加载到内存 | 大文件自动写入磁盘 |
| 文件信息 | 无(只有内容) | 有文件名、类型、大小 |
| 适用场景 | 小文件 | 大文件、需要文件信息的场景 |
可选文件上传
使用默认值 None 使文件上传变为可选:
实例
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
if not file:
return {"message": "没有上传文件"}
return {"filename": file.filename}
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
if not file:
return {"message": "没有上传文件"}
return {"filename": file.filename}
多文件上传
使用列表接收多个文件:
实例
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}
app = FastAPI()
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}
表单与文件混合上传
可以在同一个路由中同时接收表单数据和文件:
实例
from fastapi import FastAPI, File, UploadFile, Form
app = FastAPI()
@app.post("/items/")
async def create_item(
# 表单字段
name: str = Form(...),
description: str | None = Form(None),
# 文件上传
file: UploadFile | None = None,
):
result = {"name": name, "description": description}
if file:
result["filename"] = file.filename
return result
app = FastAPI()
@app.post("/items/")
async def create_item(
# 表单字段
name: str = Form(...),
description: str | None = Form(None),
# 文件上传
file: UploadFile | None = None,
):
result = {"name": name, "description": description}
if file:
result["filename"] = file.filename
return result
保存上传的文件
以下示例展示如何将上传的文件保存到磁盘:
实例
import shutil
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
# 将上传的文件保存到指定路径
with open(f"uploads/{file.filename}", "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
return {"filename": file.filename, "message": "文件上传成功"}
from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
# 将上传的文件保存到指定路径
with open(f"uploads/{file.filename}", "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
return {"filename": file.filename, "message": "文件上传成功"}
实际项目中,上传文件时应注意:1) 校验文件类型和大小;2) 使用安全的文件名(避免路径遍历攻击);3) 限制上传目录的权限;4) 对于大文件使用流式处理而非一次性读取。
小结
- 推荐使用
UploadFile,它更节省内存且提供丰富的文件信息 File适合小文件场景,直接读取为bytes- 使用
list[UploadFile]接收多文件上传 - 表单(
Form)和文件(UploadFile)可以在同一路由中使用 - 上传文件时注意安全:校验文件类型、使用安全文件名、限制大小
