介绍
需要上传头像、照片、文件时,需要实现上传文件功能
上传文件的参数类型
视图函数的参数类型 UploadFile,默认值是:File:
async def upload_save(file: UploadFile = File(...)):
UploadFile 属性
.filename:文件名.file:文件对象.size:大小file.content_type:MIME 类型,如 image/png
UploadFile 方法
await file.read():读取全部字节await file.read(size):读取指定大小分片await file.close():关闭文件
例子1
from fastapi import FastAPI,File,UploadFile
import os
# 保存附件的目录
save_dir = "./upload"
# 目录不存在就创建
os.makedirs(save_dir, exist_ok=True)
app = FastAPI()
@app.post("/upload/save")
async def upload_save(file: UploadFile = File(...)):
out_path = os.path.join(save_dir, file.filename)
# 写入磁盘
with open(out_path, "wb") as f:
f.write(await file.read())
return {"saved_path": out_path, "filename": file.filename}
测试
访问swagger:http://127.0.0.1:8000/docs ,按照下图操作:
执行结果:
缺点
await file.read() 会一次性读入内存,大文件不推荐。
解决
使用流式写入
例子2-流式写入
读一部分,写一部分,不是全部读取到内存
from fastapi import FastAPI,File,UploadFile
import os
# 保存附件的目录
save_dir = "./upload"
# 目录不存在就创建
os.makedirs(save_dir, exist_ok=True)
# 1MB 分片
READ_SIZE = 1024 * 1024
app = FastAPI()
@app.post("/upload/save")
async def upload_save(file: UploadFile = File(...)):
out_path = os.path.join(save_dir, file.filename)
# 写入磁盘
with open(out_path, "wb") as f:
# 读一部分,写一部分
while chunk := await file.read(READ_SIZE):
f.write(chunk)
return {"saved_path": out_path, "filename": file.filename}
缺点
with open(out_path, "wb") as f 是同步磁盘 IO ,当执行 f.write() 写磁盘时,会卡住整个协程事件循环;高并发上传大文件时,其他请求会被拖慢、延迟升高。
解决
- 把文件写入放到线程池(缺点:写起来有点复杂)
- 用 aiofiles 异步文件库(缺点:需要安装库)
用 aiofiles 异步写数据
关于 aiofiles 详见链接
安装
pip安装
pip install aiofiles
uv安装
uv add aiofiles
代码
from fastapi import FastAPI,File,UploadFile
import os
import aiofiles
# 保存附件的目录
save_dir = "./upload"
# 目录不存在就创建
os.makedirs(save_dir, exist_ok=True)
# 1MB 分片
READ_SIZE = 1024 * 1024
app = FastAPI()
@app.post("/upload/save")
async def upload_save(file: UploadFile = File(...)):
out_path = os.path.join(save_dir, file.filename)
# 异步写入磁盘
async with aiofiles.open(out_path, "wb") as f:
while chunk := await file.read(READ_SIZE):
await f.write(chunk)
return {"saved_path": out_path, "filename": file.filename}

