コンテンツにスキップ

リクエストファイル

クライアントがアップロードするファイルを 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 から直接継承するクラスです。

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

ヒント

ファイルボディを宣言するには、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: アップロードされた元のファイル名(例:myimage.jpg)を持つstr
  • content_type: コンテンツタイプ(MIMEタイプ/メディアタイプ)(例:image/jpeg)を持つstr
  • file: SpooledTemporaryFileファイルライクオブジェクト)。これは、ファイルライクオブジェクトを期待する他の関数やライブラリに直接渡すことができる実際の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は、ボディの正しい部分からファイルを取得する必要があることを認識します。

これらのエンコーディングとフォームフィールドの詳細については、MDNPOSTに関するWebドキュメントを参照してください。

警告

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

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

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

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は、開発者にとって便利なように、fastapi.responsesとして同じstarlette.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を使用します。