FastAPIでファイルダウンロードのAPIを作成するコードのメモ

お疲れ様です。

業務内でFastAPIを使ってcsvファイルやexcelファイルをダウンロードするAPIを作成したので、忘れないうちにメモ。
最近はWeb系のコード作成も増えていて使いどころも多くなってくるのかなと思っています。

作成コード(GitHub

github.com

実行して立ち上がったサーバにアクセスすると下記のようなWebUIが表示されます。
ボタンを押すと適当な内容が記載されたサンプルのcsvファイルかexcelファイルがダウンロードされます。

実装

FastAPIのresponse関係の仕様は公式ドキュメント参照。
fastapi.tiangolo.com

FileResponseを使用したパターン

from fastapi import FastAPI
from fastapi.responses import FileResponse

app = FastAPI()

@app.get("/download_from_file")
def download_from_file() -> FileResponse:
    """ファイルをダウンロードするAPI
    """
    return FileResponse(filepath, media_type="file/csv", filename=filepath.name)

シンプルにFastAPIの実行環境のローカルに保存されたファイルのパスを指定してそのファイルをダウンロードする形です。Pythonコードで作成したデータを一度ファイル出力してこれで返す形も取れます。

StreamingResponseを使用したパターン

import io
import pandas as pd

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

@app.get("/download_from_df")
def download_from_df(ext: str) -> StreamingResponse:
    """DataFrameを指定ファイル形式でダウンロードするAPI
       (ローカルにファイル保存せず、データをファイル化して返す)
    """
    if ext == "csv":
        stream = io.StringIO()
        sample_df.to_csv(stream, encoding='utf-8', index=False)
        stream.seek(0)
    elif ext == "xlsx":
        stream = io.BytesIO()
        sample_df.to_excel(stream, index=False)
        stream.seek(0)
    else:
        raise HTTPException(status_code=400, detail="Illegal extension.")
        
    filename = f"sample.{ext}"
    media_type = f"file/{ext}"
    
    return StreamingResponse(
        content=stream, 
        media_type=media_type,
        headers={'Content-Disposition': f'attachment; filename={filename}'}
    )

Pythonコード内で保持しているデータをローカルへのファイル出力をせずにダウンロードする方法です。ローカルに無駄なファイルが残らないのがメリットでしょうか。
Pythonの標準モジュールの「io」を使用してfile object?(正式な名前はわかりません)に変換してそれをresponseする形です。csvファイルはio.StringIO()excelファイルはio.BytesIOを使います。ファイルによって変わるようなのでここは要確認です。
コードではpandasのDataFrameをcsvファイルまたはexcelファイルとしてダウンロードできるように作成しています。また、少し捻ってクエリパラメータからダウンロードするファイルを指定する形で作成してみました。

ioモジュールについては以下公式ドキュメント参照。
組み込みのopen関数で使用されているものだとか。(私はそれほど意識したことはなく今回初めて知りました…。)
この辺の標準モジュールについても理解を深めたいなと思う今日この頃です。
docs.python.org

追記

日本語を含むファイル名に対応したパターンを作成しました。
こちらも併せてご参照ください。

fallpoke-tech.hatenadiary.jp