コンテンツへスキップ

ファイルのリクエスト

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}

情報

FileForm を直接継承するクラスです。

しかし、QueryPathFileなどをfastapiからインポートする際、それらは実際には特殊なクラスを返す関数であることを忘れないでください。

ヒント

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 (A ファイルライクな オブジェクト)。これは実際の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 の技術詳細

FastAPIUploadFile は、StarletteUploadFile を直接継承していますが、Pydantic および FastAPI の他の部分との互換性を持たせるために必要な部分が追加されています。

"フォームデータ"とは何か

HTMLフォーム (<form></form>) がサーバーにデータを送信する際、通常はJSONとは異なる「特殊な」エンコーディングを使用します。

FastAPIは、そのデータをJSONからではなく、適切な場所から確実に読み取ります。

技術的な詳細

フォームからのデータは、ファイルを含まない場合、通常「メディアタイプ」application/x-www-form-urlencoded を使用してエンコードされます。

しかし、フォームにファイルが含まれる場合、それは multipart/form-data としてエンコードされます。File を使用すると、FastAPI はボディの正しい部分からファイルを取得する必要があることを認識します。

これらのエンコーディングとフォームフィールドについてさらに詳しく知りたい場合は、MDN Web Docs for POST を参照してください。

警告

パス操作では複数の File および Form パラメータを宣言できますが、JSONとして受け取ると期待される Body フィールドを同時に宣言することはできません。これは、リクエストのボディが application/json ではなく multipart/form-data を使用してエンコードされるためです。

これは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

例えば、追加のメタデータを設定するために、UploadFile とともに File() を使用することもできます。

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 または UploadFilelist を受け取ります。

技術的な詳細

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)

まとめ

Filebytes、および UploadFile を使用して、フォームデータとして送信されるリクエストでアップロードするファイルを宣言します。