xlsx ファイルをレスポンスで返そうとした時に MIMETYPE が自動で設定されたりされなかったりした。
実行環境にエクセルが入っているかどうかで変わるっぽい。
背景
fastapi でファイルをダウンロードする API を作成した。
Content-Disposition ヘッダーを設定しておけばファイルレスポンスがいい感じになるので設定していたが、
テスト用サーバに上げるとうまく設定されない(text/plain になる)場合があったので調べた。
フロント側の処理を色々書いても解決できそうだが、 Content-Disposition ヘッダーを正しく使うのが良さそう。
fastapi の FileResponse
fastapi の FileResponse はファイルの MIMETYPE を推定してくれるが、xlsx とかで推定できないことがある。
from fastapi.responses import FileResponse
# res.media_type は 'text/csv' になる
res = FileResponse('test.csv')
# res.media_type が 'text/plain` になる場合がある
# `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` になって欲しい
res = FileResponse('test.xlsx')
基本的には FileResponse でレスポンスを作っておけば Content-Disposition ヘッダーもいい感じにしてくれる。
(type も推定されたものがセットされる)
今回推定できなかったのは xlsx。
mac でローカル実行したときは推定できたが、 windows サーバに上げると推定できなかった。
解決策
media_type を明示的に指定する。
from fastapi.responses import FileResponse
res = FileResponse('test.xlsx', media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
自動で推定してくれるところはそのままにしたかったので、実際に使ったのは以下のような形。
from fastapi.responses import FileResponse
res = FileResonse('test.xlsx')
if res.media_type == 'text/plain':
res = FileResponse('test.xlsx', media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
# インスタンス作成後に更新してもレスポンスに反映されなかった
# res.media_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
インスタンス作成後にmedia_type を変更すると、インスタンスの更新は行われるが
レスポンスに反映されなかった。
原因
python の mimetype 推定は環境によって結果が異なる。
.txt, .csv などの一般的なものはデフォルトで設定されているが、その他の mimetype 情報は
システム環境から取ってきている模様。
筆者の macbook だと /etc/apache2/mime.types のデータが参照されていた。windows だとレジストリ情報から取ってくるらしい。
今回は windows サーバで xlsx の推定ができなかった。
エクセルを入れていなかったのでレジストリに情報がなかったのかもしれない。
from mimetypes import guess_type
guess_type('test.xlsx')
# 推定できるとき
# ('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', None)
# 推定できないとき
# (None, None)
参考サイト
-
- 302行目あたり。fastapi の FileResponse のページにもあったので調べてみた。
-
-
.xlsはライブラリのデフォルトで設定されているが、.xlsxは設定されていない。
-