前言
可以使用 FastAPI 提供的 File 定義客戶端要上傳的文件
學習 File 前最好先學習 Form:https://www.cnblogs.com/poloyy/p/15311533.html
安裝 python-multipart
要用 File,需要先安裝這個庫
pip install python-multipart
FIle
File 是繼承 Form,所以可以定義和 Form 相同的元數據以及額外的驗證
上傳單個文件的栗子
#!usr/bin/env python # -*- coding:utf-8 _*- """ # author: 小菠蘿測試筆記 # blog: https://www.cnblogs.com/poloyy/ # time: 2021/9/22 9:52 上午 # file: 21_File.py """ import uvicorn from fastapi import FastAPI, File, UploadFile app = FastAPI() # file 參數類型是字節 bytes @app.post("/files/") async def create_file(file: bytes = File(...)): return {"file_size": len(file)} # file 參數類型是 UploadFile @app.post("/uploadfile/") async def create_upload_file(file: UploadFile = File(...)): result = { "filename": file.filename, "content-type": file.content_type, "read": await file.read() } return result if __name__ == "__main__": uvicorn.run(app="21_File:app", host="127.0.0.1", port=8080, reload=True, debug=True)
重點
- 因為 UploadFile 對象提供的方法都是 async 異步的,所以調用的時候都要加 await 比如 await file.read() (后面會詳解 async/await )
- 當使用異步方法時,FastAPI 在線程池中運行文件方法並等待它們
不加 await 調用 async 方法會報錯
raise ValueError(errors) ValueError: [TypeError("'coroutine' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')] WARNING: StatReload detected file change in '21_File.py'. Reloading...
file: bytes 的請求結果
file: UploadFile 的請求結果
查看 Swagger API 文檔
這樣就可以直接在 Swagger API 文檔上測試上傳文件功能啦
file: bytes
- FastAPI 將會讀取文件,接收到的內容就是文件字節
- 會將整個內容存儲在內存中,更適用於小文件
file: UploadFile
FastAPI 的 UploadFile 直接繼承了 Starlette 的 UploadFile,但增加了一些必要的部分,使其與 Pydantic 和 FastAPI 的其他部分兼容
UploadFile 相比 bytes 的優勢
- 存儲在內存中的文件達到最大大小限制,超過此限制后,它將存儲在磁盤中,可以很好地處理大文件,如圖像、視頻、大型二進制文件等,而不會消耗所有內存
- 可以從上傳的文件中獲取元數據
- 有一個類似文件的 async 異步接口
- 它公開了一個 Python SpooledTemporaryFile 對象,可以將它傳遞給其他需要文件的庫
UploadFile 具有以下屬性
- filename:str,
上傳的原始文件名,例如
myimage.jpg - content_type:str,包含 content-type(MIME type / media type),例如 image/jpeg
- file:一個 SpooledTemporaryFile(一個類似文件的對象)。 這是實際的 Python 文件,可以將其直接傳遞給其他需要“類文件”對象的函數或庫
UploadFIle 具有以下 async 異步方法
- write(data):寫入data ( str 或 bytes ) 到文件
- read(size):讀取文件的 size (int) 個字節/字符
- seek(offset):轉到文件中的字節位置 offset(int),如: await myfile.seek(0) 將轉到文件的開頭
- close():關閉文件
上傳多個文件的栗子
from typing import List @app.post("/files/") async def create_files(files: List[bytes] = File(...)): return {"file_sizes": [len(file) for file in files]} @app.post("/uploadfiles/") async def create_upload_files(files: List[UploadFile] = File(...)): return {"filenames": [file.filename for file in files]}
正確傳參的請求結果
查看 Swagger API 文檔