リクエストファイル¶
クライアントがアップロードするファイルを 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
から直接継承するクラスです。
ただし、fastapi
から Query
、Path
、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
: アップロードされた元のファイル名(例: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の技術詳細」
FastAPIのUploadFile
は、StarletteのUploadFile
から直接継承していますが、PydanticおよびFastAPIの他の部分と互換性を持たせるために必要な部分を追加しています。
「フォームデータ」とは¶
HTMLフォーム(<form></form>
)がサーバーにデータを送信する方法は、通常、そのデータに「特別な」エンコーディングを使用します。これはJSONとは異なります。
FastAPIは、JSONではなく、正しい場所からそのデータを確実に読み取ります。
「技術詳細」
フォームからのデータは、通常、ファイルが含まれていない場合、「メディアタイプ」application/x-www-form-urlencoded
を使用してエンコードされます。
ただし、フォームにファイルが含まれている場合、multipart/form-data
としてエンコードされます。File
を使用すると、FastAPIは、ボディの正しい部分からファイルを取得する必要があることを認識します。
これらのエンコーディングとフォームフィールドの詳細については、MDNのPOST
に関する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
¶
たとえば、追加のメタデータを設定するために、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は、開発者にとって便利なように、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)
まとめ¶
リクエストでアップロードされるファイル(フォームデータとして送信)を宣言するには、File
、bytes
、およびUploadFile
を使用します。