ファイルのリクエスト¶
File
を使用して、クライアントがアップロードするファイルを定義できます。
情報
アップロードされたファイルを受け取るには、まずpython-multipart
をインストールしてください。
仮想環境を作成し、アクティブ化してからインストールしてください。たとえば、
$ pip install python-multipart
これは、アップロードされたファイルが「フォームデータ」として送信されるためです。
File
をインポートする¶
fastapi
から File
と 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}
File
パラメータを定義する¶
Body
や Form
と同じようにファイルパラメータを作成します。
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
は Form
を直接継承するクラスです。
しかし、Query
、Path
、File
などを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 の技術詳細
FastAPI の UploadFile
は、Starlette の UploadFile
を直接継承していますが、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
または UploadFile
の list
を受け取ります。
技術的な詳細
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)
まとめ¶
File
、bytes
、および UploadFile
を使用して、フォームデータとして送信されるリクエストでアップロードするファイルを宣言します。