Dateien im Request¶
Sie können Dateien, die vom Client hochgeladen werden, mithilfe von File definieren.
Info
Um hochgeladene Dateien zu empfangen, installieren Sie zuerst python-multipart.
Stellen Sie sicher, dass Sie eine virtuelle Umgebung erstellen, sie aktivieren und dann das Paket installieren, zum Beispiel:
$ pip install python-multipart
Das liegt daran, dass hochgeladene Dateien als âFormulardatenâ gesendet werden.
File importieren¶
Importieren Sie File und UploadFile von fastapi:
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}
đ€ Other versions and variants
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}
Tip
Prefer to use the Annotated version if possible.
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-Parameter definieren¶
Erstellen Sie Datei-Parameter, so wie Sie es auch mit Body und Form machen wĂŒrden:
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}
đ€ Other versions and variants
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}
Tip
Prefer to use the Annotated version if possible.
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}
Info
File ist eine Klasse, die direkt von Form erbt.
Aber erinnern Sie sich, dass, wenn Sie Query, Path, File und andere von fastapi importieren, diese tatsĂ€chlich Funktionen sind, welche spezielle Klassen zurĂŒckgeben.
Tipp
Um Dateibodys zu deklarieren, mĂŒssen Sie File verwenden, da diese Parameter sonst als Query-Parameter oder Body (JSON)-Parameter interpretiert werden wĂŒrden.
Die Dateien werden als âFormulardatenâ hochgeladen.
Wenn Sie den Typ Ihrer Pfadoperation-Funktion als bytes deklarieren, wird FastAPI die Datei fĂŒr Sie auslesen, und Sie erhalten den Inhalt als bytes.
Bedenken Sie, dass das bedeutet, dass sich der gesamte Inhalt der Datei im Arbeitsspeicher befindet. Das wird fĂŒr kleinere Dateien gut funktionieren.
Aber es gibt viele FĂ€lle, in denen Sie davon profitieren, UploadFile zu verwenden.
Datei-Parameter mit UploadFile¶
Definieren Sie einen Datei-Parameter mit dem Typ 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}
đ€ Other versions and variants
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}
Tip
Prefer to use the Annotated version if possible.
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 zu verwenden, hat mehrere VorzĂŒge gegenĂŒber bytes:
- Sie mĂŒssen
File()nicht als Parameter-Defaultwert verwenden. - Es wird eine âgespoolteâ Datei verwendet:
- Eine Datei, die bis zu einem bestimmten GröĂen-Limit im Arbeitsspeicher behalten wird, und wenn das Limit ĂŒberschritten wird, auf der Festplatte gespeichert wird.
- Das bedeutet, es wird fĂŒr groĂe Dateien wie Bilder, Videos, groĂe BinĂ€rdateien, usw. gut funktionieren, ohne den ganzen Arbeitsspeicher aufzubrauchen.
- Sie können Metadaten aus der hochgeladenen Datei auslesen.
- Es hat eine dateiartige
asynchrone Schnittstelle. - Es stellt ein tatsÀchliches Python-
SpooledTemporaryFile-Objekt bereit, welches Sie direkt anderen Bibliotheken ĂŒbergeben können, die ein dateiartiges Objekt erwarten.
UploadFile¶
UploadFile hat die folgenden Attribute:
filename: Einstrmit dem ursprĂŒnglichen Namen der hochgeladenen Datei (z. B.meinbild.jpg).content_type: Einstrmit dem Inhaltstyp (MIME-Typ / Medientyp) (z. B.image/jpeg).file: EinSpooledTemporaryFile(ein dateiartiges Objekt). Das ist das tatsĂ€chliche Python-Objekt, das Sie direkt anderen Funktionen oder Bibliotheken ĂŒbergeben können, welche ein âfile-likeâ-Objekt erwarten.
UploadFile hat die folgenden asynchronen Methoden. Sie alle rufen die entsprechenden Methoden des darunterliegenden Datei-Objekts auf (wobei intern SpooledTemporaryFile verwendet wird).
write(daten): Schreibtdaten(stroderbytes) in die Datei.read(anzahl): Liestanzahl(int) bytes/Zeichen aus der Datei.seek(versatz): Geht zur Positionversatz(int) in der Datei.- Z. B. wĂŒrde
await myfile.seek(0)zum Anfang der Datei gehen. - Das ist besonders dann nĂŒtzlich, wenn Sie
await myfile.read()einmal ausfĂŒhren und dann diese Inhalte erneut auslesen mĂŒssen.
- Z. B. wĂŒrde
close(): SchlieĂt die Datei.
Da alle diese Methoden asynchron sind, mĂŒssen Sie sie âawaitâen (âerwartenâ).
Zum Beispiel können Sie innerhalb einer async Pfadoperation-Funktion den Inhalt wie folgt auslesen:
contents = await myfile.read()
Wenn Sie sich innerhalb einer normalen def-Pfadoperation-Funktion befinden, können Sie direkt auf UploadFile.file zugreifen, zum Beispiel:
contents = myfile.file.read()
Technische Details zu async
Wenn Sie die async-Methoden verwenden, fĂŒhrt FastAPI die Datei-Methoden in einem Threadpool aus und erwartet sie.
Technische Details zu Starlette
FastAPIs UploadFile erbt direkt von Starlettes UploadFile, fĂŒgt aber ein paar notwendige Teile hinzu, um es kompatibel mit Pydantic und anderen Teilen von FastAPI zu machen.
Was sind âFormulardatenâ¶
Der Weg, wie HTML-Formulare (<form></form>) die Daten zum Server senden, verwendet normalerweise eine âspezielleâ Kodierung fĂŒr diese Daten. Diese unterscheidet sich von JSON.
FastAPI stellt sicher, dass diese Daten korrekt ausgelesen werden, statt JSON zu erwarten.
Technische Details
Daten aus Formularen werden, wenn es keine Dateien sind, normalerweise mit dem âmedia typeâ application/x-www-form-urlencoded kodiert.
Sollte das Formular aber Dateien enthalten, dann werden diese mit multipart/form-data kodiert. Wenn Sie File verwenden, wird FastAPI wissen, dass es die Dateien vom korrekten Teil des Bodys holen muss.
Wenn Sie mehr ĂŒber diese Kodierungen und Formularfelder lesen möchten, besuchen Sie die MDN-Webdokumentation fĂŒr POST.
Achtung
Sie können mehrere File- und Form-Parameter in einer Pfadoperation deklarieren, aber Sie können nicht gleichzeitig auch Body-Felder deklarieren, welche Sie als JSON erwarten, da der Request den Body mittels multipart/form-data statt application/json kodiert.
Das ist keine Limitation von FastAPI, sondern Teil des HTTP-Protokolls.
Optionaler Datei-Upload¶
Sie können eine Datei optional machen, indem Sie Standard-Typannotationen verwenden und den Defaultwert auf None setzen:
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}
đ€ Other versions and variants
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}
Tip
Prefer to use the Annotated version if possible.
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}
Tip
Prefer to use the Annotated version if possible.
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 mit zusĂ€tzlichen Metadaten¶
Sie können auch File() mit UploadFile verwenden, um zum Beispiel zusÀtzliche Metadaten zu setzen:
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}
đ€ Other versions and variants
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}
Tip
Prefer to use the Annotated version if possible.
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}
Mehrere Datei-Uploads¶
Es ist auch möglich, mehrere Dateien gleichzeitig hochzuladen.
Diese werden demselben Formularfeld zugeordnet, welches mit den Formulardaten gesendet wird.
Um das zu machen, deklarieren Sie eine Liste von bytes oder UploadFiles:
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)
đ€ Other versions and variants
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)
Tip
Prefer to use the Annotated version if possible.
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)
Tip
Prefer to use the Annotated version if possible.
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)
Sie erhalten, wie deklariert, eine list von bytes oder UploadFiles.
Technische Details
Sie können auch from starlette.responses import HTMLResponse verwenden.
FastAPI bietet dieselben starlette.responses auch via fastapi.responses an, als Annehmlichkeit fĂŒr Sie, den Entwickler. Die meisten verfĂŒgbaren Responses kommen aber direkt von Starlette.
Mehrere Datei-Uploads mit zusĂ€tzlichen Metadaten¶
Und so wie zuvor können Sie File() verwenden, um zusĂ€tzliche Parameter zu setzen, sogar fĂŒr 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)
đ€ Other versions and variants
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)
Tip
Prefer to use the Annotated version if possible.
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)
Tip
Prefer to use the Annotated version if possible.
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)
Zusammenfassung¶
Verwenden Sie File, bytes und UploadFile, um hochladbare Dateien im Request zu deklarieren, die als Formulardaten gesendet werden.