Zum Inhalt

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: Ein str mit dem ursprĂŒnglichen Namen der hochgeladenen Datei (z. B. meinbild.jpg).
  • content_type: Ein str mit dem Inhaltstyp (MIME-Typ / Medientyp) (z. B. image/jpeg).
  • file: Ein SpooledTemporaryFile (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): Schreibt daten (str oder bytes) in die Datei.
  • read(anzahl): Liest anzahl (int) bytes/Zeichen aus der Datei.
  • seek(versatz): Geht zur Position versatz (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.
  • 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.