Dateien im Request¶
Mit File
können sie vom Client hochzuladende Dateien definieren.
Info
Um hochgeladene Dateien zu empfangen, installieren Sie zuerst python-multipart
.
Z. B. pip install python-multipart
.
Das, weil 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}
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}
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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}
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}
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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}
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}
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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 „Spool“-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 file-like
async
hrone 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
: Einstr
mit dem ursprünglichen Namen der hochgeladenen Datei (z. B.meinbild.jpg
).content_type
: Einstr
mit dem Inhaltstyp (MIME-Typ / Medientyp) (z. B.image/jpeg
).file
: EinSpooledTemporaryFile
(ein file-like 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 async
hronen Methoden. Sie alle rufen die entsprechenden Methoden des darunterliegenden Datei-Objekts auf (wobei intern SpooledTemporaryFile
verwendet wird).
write(daten)
: Schreibtdaten
(str
oderbytes
) 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 async
hron 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“¶
HTML-Formulare (<form></form>
) senden die Daten in einer „speziellen“ Kodierung zum Server, welche sich von JSON unterscheidet.
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 Formularfelder und ihre Kodierungen 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}
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}
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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}
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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()
zusammen 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}
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}
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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 UploadFile
s:
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)
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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)
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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
e von bytes
oder UploadFile
s.
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)
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)
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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)
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
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.