Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 3 years have 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つの方法について説明しました。簡単に使えるものからローレベルなものまで様々であったと思います。アプリケーションの仕様や前提を踏まえて、適切な方法を選択してください。
次回はファイルダウンロードと一緒に利用されることの多いファイルアップロードについて説明したいと思います。

5zm
都内のIT企業でアーキテクトのお仕事をしてます。そのご縁で「Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発」を共著させて頂きました。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away