Flaskでファイルダウンロードを実現する3つの方法

More than 1 year has passed since last update.


1. はじめに

最近、PythonでExcelファイルを作成してPJメンバに配布していましたが、手間が掛ってきたのでFlaskでダウンロードできるようにしました。今回はその際に調べたダウンロードの方法について共有したいと思います。なお、Excelファイルの作り方については以下の記事を参照ください。


2. 方法1:send_file()を利用する

ファイルをダウンロードさせる1つ目の方法はflaskのsend_file() を利用することです。

この方法はroot_path(デフォルトではflaskアプリが配置されたディレクトリ)からの相対パスで指定したファイルをダウンロードさせます。


send_file()を利用する場合

# -*- 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オプション : TrueFalseを設定。Content-Disposition: attachmentヘッダを利用する場合はTrueを設定する必要がある


  • attachment_filenameオプション : 第1引数のファイル名からファイルを変更したい場合、そのファイル名を設定


3. 方法2:send_from_directory()を利用する

ファイルをダウンロードさせる2つ目の方法はsend_from_directory() を利用することです。

send_from_directory()の場合、root_pathからの相対パスではなく、引数で指定したディレクトリからの相対パスでダウンロードさせるファイルを指定します。


send_from_directory()を利用する場合

# 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レスポンスを設定できるため、ファイルに限らずデータベースに格納されているバイナリデータ等でもダウンロードさせることができます。


make_response()を利用する場合

# 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つの方法について説明しました。簡単に使えるものからローレベルなものまで様々であったと思います。アプリケーションの仕様や前提を踏まえて、適切な方法を選択してください。

次回はファイルダウンロードと一緒に利用されることの多いファイルアップロードについて説明したいと思います。