コンテンツにスキップ

リクエストファイル

🌐 AI と人間による翻訳

この翻訳は、人間のガイドに基づいて AI によって作成されました。🤝

原文の意図を取り違えていたり、不自然な表現になっている可能性があります。🤖

AI LLM をより適切に誘導するのを手伝う ことで、この翻訳を改善できます。

英語版

File を使って、クライアントがアップロードするファイルを定義できます。

情報

アップロードされたファイルを受け取るには、まず python-multipart をインストールします。

仮想環境を作成して有効化し、次のようにインストールしてください:

$ pip install python-multipart

アップロードされたファイルは「form data」として送信されるためです。

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}
🤓 Other versions and variants

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 パラメータの定義

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}
🤓 Other versions and variants

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}

情報

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

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

豆知識

ファイルのボディを宣言するには File を使う必要があります。そうしないと、パラメータはクエリパラメータやボディ(JSON)パラメータとして解釈されます。

ファイルは「form data」としてアップロードされます。

path operation 関数 のパラメータの型を 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}
🤓 Other versions and variants

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 を使うことには bytes に対する次の利点があります:

  • パラメータのデフォルト値に File() を使う必要がありません。
  • 「spooled」ファイルを使います:
    • 最大サイズまではメモリに保持し、それを超えるとディスクに格納されるファイルです。
  • そのため、画像・動画・大きなバイナリなどの大きなファイルでも、メモリを使い果たすことなくうまく動作します。
  • アップロードされたファイルからメタデータを取得できます。
  • file-likeasync インターフェースを持ちます。
  • 実際の Python の SpooledTemporaryFile オブジェクトを公開しており、file-like オブジェクトを期待する他のライブラリにそのまま渡せます。

UploadFile

UploadFile には次の属性があります:

  • filename: アップロード時の元のファイル名を表す str(例: myimage.jpg
  • content_type: コンテントタイプ(MIME タイプ / メディアタイプ)を表す str(例: image/jpeg
  • file: SpooledTemporaryFilefile-like なオブジェクト)。これは実際の Python のファイルオブジェクトで、「file-like」オブジェクトを期待する関数やライブラリに直接渡せます。

UploadFile には次の async メソッドがあります。いずれも内部で対応するファイルメソッド(内部の SpooledTemporaryFile)を呼び出します。

  • write(data): datastr または bytes)を書き込みます。
  • read(size): sizeint)バイト/文字を読み込みます。
  • seek(offset): ファイル内のバイト位置 offsetint)に移動します。
    • 例: await myfile.seek(0) はファイルの先頭に移動します。
    • 一度 await myfile.read() を実行して、もう一度内容を読みたい場合に特に便利です。
  • close(): ファイルを閉じます。

これらはすべて async メソッドなので、await する必要があります。

例えば、asyncpath operation 関数 内では次のように内容を取得できます:

contents = await myfile.read()

通常の defpath operation 関数 内にいる場合は、UploadFile.file に直接アクセスできます。例えば:

contents = myfile.file.read()

async の技術詳細

async メソッドを使うと、FastAPI はファイルメソッドをスレッドプールで実行し、その完了を待機します。

Starlette の技術詳細

FastAPIUploadFileStarletteUploadFile を直接継承していますが、Pydantic や FastAPI の他の部分と互換にするために必要な要素を追加しています。

「Form Data」とは

HTML フォーム(<form></form>)がサーバーにデータを送る方法は、そのデータに対して通常「特別な」エンコーディングを用い、JSON とは異なります。

FastAPI は JSON ではなく、適切な場所からそのデータを読み取るようにします。

技術詳細

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

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

これらのエンコーディングやフォームフィールドの詳細は、MDN Web Docs の POST を参照してください。

注意

1 つの path operation に複数の 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}
🤓 Other versions and variants

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}

追加メタデータつきの 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}
🤓 Other versions and variants

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}

複数ファイルのアップロード

同時に複数のファイルをアップロードできます。

それらは「form data」で送信される同じ「フォームフィールド」に関連付けられます。

そのためには、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)
🤓 Other versions and variants

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)

宣言どおり、bytes または UploadFilelist を受け取ります。

技術詳細

from starlette.responses import HTMLResponse を使うこともできます。

FastAPI は利便性のため、starlette.responses と同じものを fastapi.responses として提供しています。ただし、利用可能なレスポンスの多くは Starlette から直接提供されています。

追加メタデータつきの複数ファイルアップロード

先ほどと同様に、UploadFile に対しても File() を使って追加のパラメータを設定できます:

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

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)

まとめ

リクエストでフォームデータとして送信されるアップロードファイルを宣言するには、FilebytesUploadFile を使います。