UploadFile === ###### tags: `Python / FastAPI` ###### tags: `Python`, `PyPi`, `backend`, `fastapi`, `UploadFile`, `表單`, `form` <br> [TOC] <br> ## 討論 ### FastAPI 檔案上傳錯誤說明與修正 - ### Question 關於 Python FastAPI ```python @router.post('/upload') async def upload_file(file): print('upload: ', file) ``` ``` $ curl -X POST http://localhost:8080/client/upload -F "file=@20250401-packages.zip" {"detail":[{"type":"missing","loc":["query","file"],"msg":"Field required","input":null}]} ``` 哪裡出問題? - ### gpt-4o (2025/04/16) ### ❌ 錯誤訊息 當使用以下 FastAPI 程式碼: ```python @router.post('/upload') async def upload_file(file): print('upload: ', file) ``` 搭配這個 `curl` 指令: ```bash curl -X POST http://localhost:8080/client/upload -F "file=@20250401-packages.zip" ``` 會出現以下錯誤: ``` {"detail":[{"type":"missing","loc":["query","file"],"msg":"Field required","input":null}]} ``` ### 📌 問題說明 FastAPI 預設會將參數 `file` 解讀為 **Query Parameter**,但你實際上是透過 `multipart/form-data` 上傳檔案。 若沒有使用 `UploadFile` 和 `File(...)` 來明確宣告這是檔案,FastAPI 就不會從表單中解析檔案上傳內容。 --- ### ✅ 正確做法:使用 `UploadFile` + `File(...)` 請修改你的 API 為: ```python from fastapi import APIRouter, UploadFile, File router = APIRouter() @router.post('/upload') async def upload_file(file: UploadFile = File(...)): print('upload:', file.filename) return {"filename": file.filename} ``` 這樣 FastAPI 才會從表單欄位中解析上傳的檔案。 --- ### ✅ 附加需求(可選) 如果你還要額外上傳欄位,例如 `token`: ```python from fastapi import Form @router.post('/upload') async def upload_file( file: UploadFile = File(...), token: str = Form(...) ): ... ``` 對應的 `curl`: ``` curl -X POST http://localhost:8080/client/upload \ -F "file=@20250401-packages.zip" \ -F "token=abc123" ``` --- ### 🔧 延伸功能建議 你可以進一步擴充功能,如下: - ✅ 檔案上傳解壓縮 - ✅ 上傳後自動觸發後端流程 - ✅ Token 自動產生與驗證 - ✅ 支援多用戶、多組 Token 與權限 - ✅ Swagger UI 支援測試 - ✅ 解壓縮後回傳檔案清單 如需,我可以幫你模組化實作完整專案架構。 <br> ### RuntimeError: Form data requires "python-multipart" to be installed. - ### Question RuntimeError: Form data requires "python-multipart" to be installed. You can install "python-multipart" with: pip install python-multipart - ### gpt-4o (2025/04/16) > FastAPI 檔案上傳錯誤與解法 ### 錯誤訊息 當你在 FastAPI 中使用 `UploadFile` 或 `Form` 接收 multipart/form-data 時,若出現以下錯誤: ``` RuntimeError: Form data requires "python-multipart" to be installed. You can install "python-multipart" with: pip install python-multipart ``` 表示你尚未安裝處理表單上傳所需的依賴套件。 --- ### ✅ 解法 請安裝 `python-multipart`: ``` pip install python-multipart ``` 這個套件會讓 FastAPI 能解析上傳表單,特別是 `UploadFile` 和 `Form(...)` 所需的 `multipart/form-data` 格式。 --- ### 📦 建議 dependencies 若你正在開發 FastAPI 專案,建議將以下內容加入 `requirements.txt` 或安裝: ``` fastapi uvicorn python-multipart ``` 你也可以一鍵安裝全部推薦套件: ``` pip install "fastapi[all]" ``` 這樣會連 `python-multipart`、`jinja2`、`pydantic-settings` 等都一併裝好。 --- ### ✅ 正確的上傳路由範例 ```python= from fastapi import APIRouter, UploadFile, File router = APIRouter() @router.post('/upload') async def upload_file(file: UploadFile = File(...)): print('upload:', file.filename) return {"filename": file.filename} ``` --- ### ✅ 對應的 curl 測試指令 ``` curl -X POST http://localhost:8080/client/upload -F "file=@20250401-packages.zip" ``` --- 有了這些設定後,FastAPI 就能正確處理 `multipart/form-data` 檔案上傳。 --- ### 🔧 延伸功能(可選): 你也可以加入以下功能來擴充你的 API: - ✅ Token 驗證 - ✅ 上傳後自動解壓縮 ZIP - ✅ 回傳檔案列表 - ✅ Swagger UI 自動化說明文件 - ✅ 多使用者權限控管與 Token 過期機制 如需範例,我可以提供分模組結構的專案範本。 <br> ### 📦 FastAPI 中的 `File(...)` 與 `...` 的意思 - ### gpt-4o (2025/04/16) ### 🔍 `File(...)` 是什麼? 在 FastAPI 中,我們用 `File(...)` 來處理從 `multipart/form-data` 上傳的檔案。 範例: ```python from fastapi import File, UploadFile async def upload_file(file: UploadFile = File(...)): ... ``` 這樣的寫法會讓 FastAPI 知道: - `file` 是從上傳表單來的檔案。 - 使用者 **必須提供這個檔案**,否則會報錯。 --- ### ✅ `...` 是什麼? `...` 是 Python 的內建物件,名稱是 `Ellipsis`,在 FastAPI / Pydantic 中有特別用途: - `...` 表示「這個欄位是 **必填** 的」。 - 等同於告訴 FastAPI:「這個欄位沒有預設值,也不能省略」。 範例比較: ``` # 必填 file: UploadFile = File(...) # 選填(預設為 None) file: UploadFile = File(None) ``` --- ### 📥 FastAPI 接收不同資料來源對照表 | FastAPI 類型 | 資料來源 | 寫法範例 | 說明 | |--------------|----------------------|------------------------|------------------------------| | `Query(...)` | URL 查詢字串 | `?id=123` | 來自網址後方的參數 | | `Form(...)` | 表單欄位(非檔案) | `application/x-www-form-urlencoded` 或 `multipart/form-data` | 接收一般表單欄位 | | `File(...)` | 上傳檔案(表單檔案) | `multipart/form-data` | 專門處理檔案上傳 | | `Body(...)` | JSON 請求主體 | `application/json` | 接收 JSON 結構資料 | --- ### 🚨 常見錯誤 如果你這樣寫: ```python async def upload_file(file): ... ``` FastAPI 會預設你是從 `query` 接收 `file`,而不是從表單上傳,導致出現錯誤訊息: ```json {"detail":[{"type":"missing","loc":["query","file"],"msg":"Field required","input":null}]} ``` --- ### ✅ 解法總結 - 要處理上傳檔案:使用 `file: UploadFile = File(...)` - `...` 表示這是 **必填欄位** - 要記得安裝必要套件: ```bash pip install python-multipart ``` --- 是否也需要我幫你補上 `Form(...)` 的對應範例一起整理? <br> {%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}