1. はじめに
最近、PythonでExcelファイルを作成してPJメンバに配布していましたが、手間が掛ってきたのでFlaskでダウンロードできるようにしました。今回はその際に調べたダウンロードの方法について共有したいと思います。なお、Excelファイルの作り方については以下の記事を参照ください。
- PythonでGitBucketのIssueをExcel出力する(Excel出力編)
- PythonでGitBucketのIssueをExcel出力する(その後)
- PythonのoepnpyxlでテンプレートファイルからExcel帳票を作成する
2. 方法1:send_file()を利用する
ファイルをダウンロードさせる1つ目の方法はflaskのsend_file()
を利用することです。
この方法はroot_path
(デフォルトではflaskアプリが配置されたディレクトリ)からの相対パスで指定したファイルをダウンロードさせます。
# -*- coding: utf-8 -*-
from flask import Flask, send_file, make_response, send_from_directory
# ★ポイント1
# 2007 Office system ファイル形式の MIME タイプをサーバーで登録する
# https://technet.microsoft.com/ja-jp/library/ee309278(v=office.12).aspx
XLSX_MIMETYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
# flask
app = Flask(__name__, static_folder=None)
# rest api
# case 1 : send_file()
@app.route('/report1/<string:report_id>', methods=['GET'])
def report1(report_id):
# ★ポイント2
downloadFileName = 'report1_' + report_id + '.xlsx'
downloadFile = 'demo1.xlsx'
# ★ポイント3
return send_file(downloadFile, as_attachment = True, \
attachment_filename = downloadFileName, \
mimetype = XLSX_MIMETYPE)
# main
if __name__ == "__main__":
print(app.url_map)
app.run(host='localhost', port=3000)
★ポイント1
ファイルをダウンロードした際、クライアント側で適切にファイルを処理できるようにmimetypeを定義します。
Excelファイル(拡張子xlsx)のmimetypeはマイクロソフトのサイトを参考にしました。
★ポイント2
ファイルをダウンロードした際、クライアントに伝える(Webブラウザだとダウンロードダイアログに表示される)ファイル名を定義します。
実際にダウンロードさせるファイル名をroot_path
からの相対パスで定義します。サンプルではroot_path
に配置されているdemo1.xlsx
ファイルをダウンロードさせることにします。
★ポイント3
send_file()
でroot_path
配下に配置されているファイルをダウンロードさせます。第1引数はダウンロード対象のファイル、第2引数以降はオプションになります。
- 第1引数 : ダウンロード対象のファイル(
root_path
からの相対パスで指定) -
mimetype
オプション : ダウンロードファイルのmimetypeを設定 -
as_attachment
オプション :True
かFalse
を設定。Content-Disposition: attachment
ヘッダを利用する場合はTrue
を設定する必要がある -
attachment_filename
オプション : 第1引数のファイル名からファイルを変更したい場合、そのファイル名を設定
3. 方法2:send_from_directory()を利用する
ファイルをダウンロードさせる2つ目の方法はsend_from_directory() を利用することです。
send_from_directory()
の場合、root_path
からの相対パスではなく、引数で指定したディレクトリからの相対パスでダウンロードさせるファイルを指定します。
# case 2 : send_from_directory()
# ★ポイント1
# ex) set DOWNLOAD_DIR_PATH=C:/tmp/flaskDownloadDir
DOWNLOAD_DIR_PATH = os.getenv("DOWNLOAD_DIR_PATH")
@app.route('/report2/<string:report_id>', methods=['GET'])
def report2(report_id):
# ★ポイント2
downloadFileName = 'report2_' + report_id + '.xlsx'
downloadFile = 'demo2.xlsx'
# ★ポイント3
return send_from_directory(DOWNLOAD_DIR_PATH, downloadFile, \
as_attachment = True, attachment_filename = downloadFileName, \
mimetype = XLSX_MIMETYPE)
★ポイント1
send_from_directory()
で指定するダウンロード対象のファイルが格納されたディレクトリを定義します。環境に応じて変更できるように、サンプルでは環境変数から取得するようにしました。
★ポイント2
send_file()
と同様にダウンロード時のファイル名とダウンロード対象のファイルを定義します。ファイルは★ポイント1で説明したディレクトリからの相対パスで定義します。
★ポイント3
send_from_directory()
で指定したディレクトリに配置されているファイルをダウンロードさせます。第1引数はファイルが配置されているディレクトリのパス、第2引数はダウンロード対象のファイル、第3引数以降はオプション(send_file()
を参照)になります。
4. 方法3:make_response()を利用する
ファイルをダウンロードさせる3つ目の方法はmake_response() を利用することです。
HTTPレスポンスを設定できるため、ファイルに限らずデータベースに格納されているバイナリデータ等でもダウンロードさせることができます。
# case 3 : make_response()
@app.route('/report3/<string:report_id>', methods=['GET'])
def report3(report_id):
# ★ポイント1
response = make_response()
# ★ポイント2
response.data = open("demo3.xlsx", "rb").read()
# ★ポイント3
downloadFileName = 'report3_' + report_id + '.xlsx'
response.headers['Content-Disposition'] = 'attachment; filename=' + downloadFileName
# ★ポイント4
response.mimetype = XLSX_MIMETYPE
return response
★ポイント1
make_response()
でレスポンスオブジェクトを作成します。
★ポイント2
ダウンロードデータをレスポンスオブジェクトのdata
に設定します。
サンプルではファイル(demo3.xlsx)から読み込んだバイナリデータを設定していますが、データベースや連携システムから取得したデータでも可能です。
★ポイント3
前述の2つの方法と同じくダウンロード時のファイル名を定義します。
レスポンスヘッダは設定されないためContent-Disposition: attachment
ヘッダを手動で設定します。
★ポイント4
レスポンスオブジェクトのmimetype
にダウンロードファイルのmimetype
を設定し、作成したレスポンスオブジェクトを戻り値として返却します。
5. さいごに
今回はFlaskでファイルダウンロードを実現する3つの方法について説明しました。簡単に使えるものからローレベルなものまで様々であったと思います。アプリケーションの仕様や前提を踏まえて、適切な方法を選択してください。
次回はファイルダウンロードと一緒に利用されることの多いファイルアップロードについて説明したいと思います。