17.FastAPI 表單數據

17.FastAPI 表單數據

如果接收的數據不是JSON格式,而是表單字段,則需要使用Form。在FastAPI中,要使用Form,需要事先安裝python-multipart,執行如下命令:

pip install python-multipart

Form參數與Path、Query、Body一樣,從fastapi導入。其使用方法相同。

17.1使用Form參數from fastapi import FastAPIfrom fastapi import Formapp = FastAPI()@app.post(path='/login')async def login(uacc: str = Form(...), upwd: str = Form(...)): res = False if uacc == 'admin' and upwd == 'admin': res = True return {'res': res}

執行請求:

curl -d "uacc=admin&upwd=123" -X POST http://127.0.0.1:8000/login{"res":false}curl -d "uacc=admin&upwd=admin" -X POST http://127.0.0.1:8000/login{"res":true}

在使用Form參數時,需要注意:可以在一個路由操作中聲明多個 Form 參數,但不能同時聲明要接收 JSON 的 Body 字段。因爲此時請求體的編碼是 application/x-www-form-urlencoded,不是 application/json;這不是 FastAPI 的問題,而是 HTTP 協議的規定。

17.2使用File參數

在web開發中,文件上傳的需求是肯定會出現的,FastAPI通過多種方式支持文件上傳操作。首先可以使用與Form、Body等相同的File,File直接繼承自Form類。代碼如下:

from fastapi import FastAPIfrom fastapi import Fileapp = FastAPI()@app.post(path='/upload')async def login(file: bytes = File(...)): with open('test.png', 'wb') as f: f.write(file) return {'file_size': len(file)}

執行請求:

curl -F "[email protected]" -X POST http://127.0.0.1:8000/upload{"file_size":10731}

通過查看項目所在文件夾,其下的test.png文件與demo.png文件相同。

在FastAPI中,聲明文件體必須使用 File,否則,FastAPI 會把該參數當作查詢參數或請求體(JSON)參數;文件作爲表單數據上傳;如果路由操作函數參數的類型聲明爲 bytes,FastAPI 將以 bytes 形式讀取和接收文件內容,這種方式把文件的所有內容都存儲在內存裏,適用于小文件。所以,在大多數情況下,我們會使用 UploadFile。

17.3UploadFile類型

定義 File 參數時使用 UploadFile 類型,UploadFile 的屬性如下:

  • filename:上傳文件的文件名字符串;
  • content_type:內容類型(MIME 類型 / 媒體類型)字符串(str),如:image/jpeg;
  • file: 文件,可直接傳遞給其他file對象的函數或支持庫。

UploadFile 支持以下 async 方法:

  • write(data):把 data (str 或 bytes)寫入文件;
  • read(size):按指定數量的字節或字符(size (int))讀取文件內容;
  • seek(offset):移動至文件 offset (int)字節處的位置;例如,await myfile.seek(0) 移動到文件開頭;執行 await myfile.read() 後,需再次讀取已讀取內容時,這種方法特別好用;
  • close():關閉文件。

因爲上述方法都是 async 方法,要搭配 await 使用。

代碼如下:

from fastapi import FastAPIfrom fastapi import Filefrom fastapi import UploadFileapp = FastAPI()@app.post(path='/upload')async def login(file: UploadFile = File(...)): with open('test.png', 'wb') as f: f.write(await file.read()) return {'file_name': file.filename}

執行請求:

curl -F "[email protected]" -X POST http://127.0.0.1:8000/upload{"file_name":"demo.png"}

通過查看項目所在文件夾,其下的test.png文件與demo.png文件相同。

使用UploadFile與 bytes 相比,其優勢:

  • 使用 spooled 文件:存儲在內存的文件超出最大上限時,FastAPI 會把文件存入磁盤;
  • 這種方式更適于處理圖像、視頻、二進制文件等大型文件,好處是不會占用所有內存;
  • 可獲取上傳文件的元數據;
  • 自帶 file-like async 接口;
  • 暴露的 Python SpooledTemporaryFile 對象,可直接傳遞給其他預期「file-like」對象的庫。

17.4多文件上傳

FastAPI支持同時上傳多個文件,可以使用一個表單字段上傳多個文件,此時,路由操作函數要聲明爲包含bytes或UploadFile的列表。代碼如下:

from fastapi import FastAPIfrom fastapi import Filefrom fastapi import UploadFilefrom typing import Listapp = FastAPI()@app.post(path='/upload')async def upload(files: List[bytes] = File(...)): sizes = [len(file) for file in files] index = 0 for file in files: with open("{0}.png".format(index), 'wb') as f: f.write(file) index += 1 return [email protected](path='/upload_file')async def upload_file(files: List[UploadFile] = File(...)): names = [file.filename for file in files] for file in files: with open(file.filename, 'wb') as f: f.write(await file.read()) return names17.5同時請求表單與文件

在FastAPI中,支持同時使用表單和文件。但可以在一個路徑操作中聲明 File 和 Form 參數,但不能同時聲明要接收 JSON 的 Body 字段。因爲此時請求體的編碼是 multipart/form-data,不是 application/json。

from fastapi import FastAPIfrom fastapi import Formfrom fastapi import Filefrom fastapi import UploadFilefrom typing import Listapp = FastAPI()@app.post(path='/upload')async def upload(files: List[bytes] = File(...), desc: str = Form(...)): print(desc) sizes = [len(file) for file in files] index = 0 for file in files: with open("{0}.png".format(index), 'wb') as f: f.write(file) index += 1 return [email protected](path='/upload_file')async def upload_file(files: List[UploadFile] = File(...), desc: str = Form(...)): print(desc) names = [file.filename for file in files] for file in files: with open(file.filename, 'wb') as f: f.write(await file.read()) return names

執行請求:

curl -F "desc=images" -F "[email protected]" -F "[email protected]" -X POST http://127.0.0.1:8000/upload[10731,50279]curl -F "desc=images" -F "[email protected]" -F "[email protected]" -X POST http://127.0.0.1:8000/upload_file["demo.png","elephant.png"]

後台print輸出:

imagesINFO: 127.0.0.1:52608 - "POST /upload HTTP/1.1" 200 OKimagesINFO: 127.0.0.1:52611 - "POST /upload_file HTTP/1.1" 200 OK