---
# System prepended metadata

title: UploadFile
tags: [UploadFile, form, Python / FastAPI, PyPi, 表單, backend, Python, fastapi]

---

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 %}