跳至內容

請求檔案

您可以使用 File 定義要由客戶端上傳的檔案。

資訊

要接收上傳的檔案,請先安裝 python-multipart

請確保您建立一個 虛擬環境,啟動它,然後安裝它,例如:

$ pip install python-multipart

這是因為上傳的檔案是以「表單資料」的形式傳送的。

匯入 File

fastapi 匯入 FileUploadFile

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}
from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

提示

如果可能,建議使用 Annotated 版本。

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes = File()):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

定義 File 參數

以與 BodyForm 相同的方式建立檔案參數

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}
from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

提示

如果可能,建議使用 Annotated 版本。

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes = File()):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

資訊

File 是一個直接繼承自 Form 的類別。

但請記住,當您從 fastapi 匯入 QueryPathFile 等時,這些實際上是返回特殊類別的函式。

提示

要宣告檔案主體,您需要使用 File,否則參數將被解釋為查詢參數或主體 (JSON) 參數。

檔案將以「表單資料」的形式上傳。

如果您將路徑操作函式參數的類型宣告為 bytesFastAPI 將會為您讀取檔案,您將會收到 bytes 類型的內容。

請記住,這表示整個內容將儲存在記憶體中。這適用於小型檔案。

但在某些情況下,您可能會受益於使用 UploadFile

使用 UploadFile 的檔案參數

使用 UploadFile 類型定義檔案參數

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}
from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

提示

如果可能,建議使用 Annotated 版本。

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes = File()):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
    return {"filename": file.filename}

使用 UploadFilebytes 有幾個優點

  • 您不必在參數的預設值中使用 File()
  • 它使用「暫存」檔案
    • 一個儲存在記憶體中的檔案,大小上限有限制,超過此限制後,它將儲存在磁碟中。
  • 這表示它適用於影像、影片、大型二進位檔案等大型檔案,而不會耗盡所有記憶體。
  • 您可以從上傳的檔案中取得中繼資料。
  • 它有一個 類似檔案的 async 介面。
  • 它公開了一個實際的 Python SpooledTemporaryFile 物件,您可以直接將其傳遞給需要類似檔案物件的其他函式庫。

UploadFile

UploadFile 具有以下屬性

  • filename:一個包含上傳的原始檔案名稱的 str(例如 myimage.jpg)。
  • content_type:一個包含內容類型(MIME 類型 / 媒體類型)的 str(例如 image/jpeg)。
  • file:一個 SpooledTemporaryFile(一個 類似檔案的 物件)。這是實際的 Python 檔案物件,您可以直接將其傳遞給需要「類似檔案」物件的其他函式或函式庫。

UploadFile 具有以下 async 方法。它們都會呼叫底層的對應檔案方法(使用內部的 SpooledTemporaryFile)。

  • write(data):將 datastrbytes)寫入檔案。
  • read(size):讀取檔案的 sizeint)位元組/字元。
  • seek(offset):移動到檔案中的位元組位置 offsetint)。
    • 例如,await myfile.seek(0) 會移動到檔案的開頭。
    • 如果您執行一次 await myfile.read() 然後需要再次讀取內容,這會特別有用。
  • close():關閉檔案。

由於所有這些方法都是 async 方法,因此您需要「await」它們。

例如,在 async *路徑操作函式* 中,您可以使用以下方式取得內容

contents = await myfile.read()

如果您在一般的 def *路徑操作函式* 中,您可以直接存取 UploadFile.file,例如

contents = myfile.file.read()

async 技術細節」

當您使用 async 方法時,**FastAPI** 會在執行緒池中執行檔案方法並等待它們。

「Starlette 技術細節」

**FastAPI** 的 UploadFile 直接繼承自 **Starlette** 的 UploadFile,但新增了一些必要的元件,使其與 **Pydantic** 和 FastAPI 的其他元件相容。

什麼是「表單資料」

HTML 表單 (<form></form>) 將資料傳送到伺服器的方式通常會使用一種「特殊」的編碼方式,它與 JSON 不同。

**FastAPI** 會確保從正確的位置讀取該資料,而不是 JSON。

「技術細節」

當表單資料不包含檔案時,通常使用「媒體類型」application/x-www-form-urlencoded 編碼。

但是,當表單包含檔案時,它會被編碼為 multipart/form-data。如果您使用 File,**FastAPI** 會知道它必須從訊息主體的正確部分取得檔案。

如果您想進一步了解這些編碼和表單欄位,請前往 MDN 網頁文件,了解 POST

警告

您可以在 *路徑操作* 中宣告多個 FileForm 參數,但您不能同時宣告預期以 JSON 接收的 Body 欄位,因為請求將使用 multipart/form-data 而不是 application/json 編碼訊息主體。

這不是 **FastAPI** 的限制,它是 HTTP 協定的一部分。

選用檔案上傳

您可以使用標準類型註釋並將預設值設定為 None 來使檔案成為選用。

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[bytes | None, File()] = None):
    if not file:
        return {"message": "No file sent"}
    else:
        return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
    if not file:
        return {"message": "No upload file sent"}
    else:
        return {"filename": file.filename}
from typing import Annotated, Union

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[Union[bytes, None], File()] = None):
    if not file:
        return {"message": "No file sent"}
    else:
        return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: Union[UploadFile, None] = None):
    if not file:
        return {"message": "No upload file sent"}
    else:
        return {"filename": file.filename}
from typing import Union

from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[Union[bytes, None], File()] = None):
    if not file:
        return {"message": "No file sent"}
    else:
        return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: Union[UploadFile, None] = None):
    if not file:
        return {"message": "No upload file sent"}
    else:
        return {"filename": file.filename}

提示

如果可能,建議使用 Annotated 版本。

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes | None = File(default=None)):
    if not file:
        return {"message": "No file sent"}
    else:
        return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
    if not file:
        return {"message": "No upload file sent"}
    else:
        return {"filename": file.filename}

提示

如果可能,建議使用 Annotated 版本。

from typing import Union

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: Union[bytes, None] = File(default=None)):
    if not file:
        return {"message": "No file sent"}
    else:
        return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(file: Union[UploadFile, None] = None):
    if not file:
        return {"message": "No upload file sent"}
    else:
        return {"filename": file.filename}

帶有額外中繼資料的 UploadFile

您也可以將 File()UploadFile 一起使用,例如,設定額外的中繼資料

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[bytes, File(description="A file read as bytes")]):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[UploadFile, File(description="A file read as UploadFile")],
):
    return {"filename": file.filename}
from fastapi import FastAPI, File, UploadFile
from typing_extensions import Annotated

app = FastAPI()


@app.post("/files/")
async def create_file(file: Annotated[bytes, File(description="A file read as bytes")]):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(
    file: Annotated[UploadFile, File(description="A file read as UploadFile")],
):
    return {"filename": file.filename}

提示

如果可能,建議使用 Annotated 版本。

from fastapi import FastAPI, File, UploadFile

app = FastAPI()


@app.post("/files/")
async def create_file(file: bytes = File(description="A file read as bytes")):
    return {"file_size": len(file)}


@app.post("/uploadfile/")
async def create_upload_file(
    file: UploadFile = File(description="A file read as UploadFile"),
):
    return {"filename": file.filename}

多個檔案上傳

可以同時上傳多個檔案。

它們將與使用「表單資料」傳送的相同「表單欄位」相關聯。

要使用它,請宣告一個 bytesUploadFile 的列表

from typing import Annotated

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.post("/files/")
async def create_files(files: Annotated[list[bytes], File()]):
    return {"file_sizes": [len(file) for file in files]}


@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
    return {"filenames": [file.filename for file in files]}


@app.get("/")
async def main():
    content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)
from typing import List

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
from typing_extensions import Annotated

app = FastAPI()


@app.post("/files/")
async def create_files(files: Annotated[List[bytes], File()]):
    return {"file_sizes": [len(file) for file in files]}


@app.post("/uploadfiles/")
async def create_upload_files(files: List[UploadFile]):
    return {"filenames": [file.filename for file in files]}


@app.get("/")
async def main():
    content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)

提示

如果可能,建議使用 Annotated 版本。

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse

app = FastAPI()


@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]):
    return {"filenames": [file.filename for file in files]}


@app.get("/")
async def main():
    content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)

提示

如果可能,建議使用 Annotated 版本。

from typing import List

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse

app = FastAPI()


@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]):
    return {"filenames": [file.filename for file in files]}


@app.get("/")
async def main():
    content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)

如所宣告,您將收到一個 bytesUploadFilelist

「技術細節」

您也可以使用 from starlette.responses import HTMLResponse

**FastAPI** 提供與 starlette.responses 相同的 fastapi.responses,只是為了方便開發人員。但大多數可用的回應都直接來自 Starlette。

帶有額外中繼資料的多個檔案上傳

與之前相同,您可以使用 File() 設定額外參數,即使是針對 UploadFile 也一樣

from typing import Annotated

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.post("/files/")
async def create_files(
    files: Annotated[list[bytes], File(description="Multiple files as bytes")],
):
    return {"file_sizes": [len(file) for file in files]}


@app.post("/uploadfiles/")
async def create_upload_files(
    files: Annotated[
        list[UploadFile], File(description="Multiple files as UploadFile")
    ],
):
    return {"filenames": [file.filename for file in files]}


@app.get("/")
async def main():
    content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)
from typing import List

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
from typing_extensions import Annotated

app = FastAPI()


@app.post("/files/")
async def create_files(
    files: Annotated[List[bytes], File(description="Multiple files as bytes")],
):
    return {"file_sizes": [len(file) for file in files]}


@app.post("/uploadfiles/")
async def create_upload_files(
    files: Annotated[
        List[UploadFile], File(description="Multiple files as UploadFile")
    ],
):
    return {"filenames": [file.filename for file in files]}


@app.get("/")
async def main():
    content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)

提示

如果可能,建議使用 Annotated 版本。

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.post("/files/")
async def create_files(
    files: list[bytes] = File(description="Multiple files as bytes"),
):
    return {"file_sizes": [len(file) for file in files]}


@app.post("/uploadfiles/")
async def create_upload_files(
    files: list[UploadFile] = File(description="Multiple files as UploadFile"),
):
    return {"filenames": [file.filename for file in files]}


@app.get("/")
async def main():
    content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)

提示

如果可能,建議使用 Annotated 版本。

from typing import List

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse

app = FastAPI()


@app.post("/files/")
async def create_files(
    files: List[bytes] = File(description="Multiple files as bytes"),
):
    return {"file_sizes": [len(file) for file in files]}


@app.post("/uploadfiles/")
async def create_upload_files(
    files: List[UploadFile] = File(description="Multiple files as UploadFile"),
):
    return {"filenames": [file.filename for file in files]}


@app.get("/")
async def main():
    content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """
    return HTMLResponse(content=content)

摘要

使用 FilebytesUploadFile 來宣告要在請求中上傳的檔案,以表單資料的形式發送。