請求檔案¶
您可以使用 File
定義要由客戶端上傳的檔案。
資訊
要接收上傳的檔案,請先安裝 python-multipart
。
請確保您建立一個 虛擬環境,啟動它,然後安裝它,例如:
$ pip install python-multipart
這是因為上傳的檔案是以「表單資料」的形式傳送的。
匯入 File
¶
從 fastapi
匯入 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()]):
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
參數¶
以與 Body
或 Form
相同的方式建立檔案參數
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
匯入 Query
、Path
、File
等時,這些實際上是返回特殊類別的函式。
提示
要宣告檔案主體,您需要使用 File
,否則參數將被解釋為查詢參數或主體 (JSON) 參數。
檔案將以「表單資料」的形式上傳。
如果您將路徑操作函式參數的類型宣告為 bytes
,FastAPI 將會為您讀取檔案,您將會收到 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}
使用 UploadFile
比 bytes
有幾個優點
- 您不必在參數的預設值中使用
File()
。 - 它使用「暫存」檔案
- 一個儲存在記憶體中的檔案,大小上限有限制,超過此限制後,它將儲存在磁碟中。
- 這表示它適用於影像、影片、大型二進位檔案等大型檔案,而不會耗盡所有記憶體。
- 您可以從上傳的檔案中取得中繼資料。
- 它有一個 類似檔案的
async
介面。 - 它公開了一個實際的 Python
SpooledTemporaryFile
物件,您可以直接將其傳遞給需要類似檔案物件的其他函式庫。
UploadFile
¶
UploadFile
具有以下屬性
filename
:一個包含上傳的原始檔案名稱的str
(例如myimage.jpg
)。content_type
:一個包含內容類型(MIME 類型 / 媒體類型)的str
(例如image/jpeg
)。file
:一個SpooledTemporaryFile
(一個 類似檔案的 物件)。這是實際的 Python 檔案物件,您可以直接將其傳遞給需要「類似檔案」物件的其他函式或函式庫。
UploadFile
具有以下 async
方法。它們都會呼叫底層的對應檔案方法(使用內部的 SpooledTemporaryFile
)。
write(data)
:將data
(str
或bytes
)寫入檔案。read(size)
:讀取檔案的size
(int
)位元組/字元。seek(offset)
:移動到檔案中的位元組位置offset
(int
)。- 例如,
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
。
警告
您可以在 *路徑操作* 中宣告多個 File
和 Form
參數,但您不能同時宣告預期以 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}
多個檔案上傳¶
可以同時上傳多個檔案。
它們將與使用「表單資料」傳送的相同「表單欄位」相關聯。
要使用它,請宣告一個 bytes
或 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()]):
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)
如所宣告,您將收到一個 bytes
或 UploadFile
的 list
。
「技術細節」
您也可以使用 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)
摘要¶
使用 File
、bytes
和 UploadFile
來宣告要在請求中上傳的檔案,以表單資料的形式發送。