LoginSignup
5
2

More than 1 year has passed since last update.

FastAPIでExcelデータを作成し一時保存せずにそのままレスポンスする方法

Posted at

前提

FastAPIは通常はただのdictやPydanticで定義したモデルをもとにレスポンスをJSONに変換して返却するが、他の形式のレスポンスを返す方法も存在する。

例えば、GETリクエストで形式を指定してJSONなら通常通りJSONを返却し、Excelならxlsx形式のレスポンスを返却するようなエンドポイントがあるとする。この場合、既にファイルが作成済みであればFileResponseを利用して直接レスポンスを返却することはドキュメントにある通り簡単にできるが、問題はリクエストを受けてからOpenPyXLでxlsx形式データを作成しそのデータを保存せずにそのまま返却するにはどうすればいいかである。

確認したバージョンは以下の通り。
* FastAPI: 0.68.1
* OpenPyXL: 2.6.2

方法

通常、OpenPyXLでは次のようにワークブックを作って、ファイル名を指定して保存する。

workbook = Workbook()
# ...ここでシートをつくって色々書きこむ...
workbook.save("xlsxファイル名")

save()メソッドにはファイルパスを指定するのが普通だが、ここにfile-likeオブジェクトを指定するとそのオブジェクトに対してデータを書き込むことができる。

そこで、次のようにBytesIOオブジェクトを指定してデータを保存する。

xlsx = BytesIO()
workbook = Workbook()
# ...ここでシートをつくって色々書きこむ...
workbook.save(xlsx)

一方、FastAPIではStreamingResponseを使ってfile-likeオブジェクトをレスポンスに返却することができるため、組み合わせると次のようになる。

from urllib.parse import quote

# ...中略...

@app.get("/hoge", response_class=StreamingResponse)
def get_hoge_as_xlsx():
    xlsx = BytesIO()
    workbook = Workbook()
    # ...ここでシートをつくって色々書きこむ...
    workbook.save(xlsx)
    xlsx.seek(0) # !!! 注意 !!!
    filename = quote("ほげほげ.xlsx")
    return StreamingResponse(
        content=xlsx,
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
        media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    )

StreamingResponseは現在のシーク位置からレスポンスを作成するので、OpenPyXLでsaveした後は先頭に戻す(seek(0))必要がある。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2