HOWTO · Flask
Flask を使用してファイルをダウンロードする
この説明では、Flask の send_file 関数を使用して、ファイルを添付ファイルとしてダウンロードするためのダウンロード可能なリンクを作成する方法を学習します。
このページの内容
Flaskでリンクからファイルをダウンロードできるようにするには、send_file(path, as_attachment=True)を返すルートを定義し、テンプレートのリンクからurl_forでそのルートを指定します。ファイルは、ルートが使うパスに存在している必要があります。
Flaskのダウンロードルートからファイルを返す
send_fileはファイルを含むレスポンスを作成します。ビュー関数はそのレスポンスを返す必要があります。returnを付けずにsend_fileを呼び出すだけでは、Flaskに意図したレスポンスが渡りません。as_attachment=Trueを指定すると、Flaskはファイルを添付ファイルとして送信するため、ブラウザーはページとして表示するのではなく、ダウンロードするよう促します。
テンプレートからダウンロードルートにリンクする
ルートのエンドポイント名をurl_forに指定します。既定では、Flaskはビュー関数の名前をエンドポイントとして使います。そのため、以下の例ではリンクと関数の両方でdownload_fileを使っています。URLパス(/download)とエンドポイント名は別のものです。url_forはテンプレートの描画時にエンドポイントを参照します。GETリンクにしても、エンドポイント名の誤記や未登録の問題は解決しません。
テンプレートは、app.pyと同じ階層にあるtemplatesという名前のフォルダーに配置します。
このセクションのコードと表示されるラベルや出力は英語のままです。
<h2>Download the sample file</h2>
<p><a href="{{ url_for('download_file') }}">Download</a></p>
信頼できるファイルパスと添付ファイルの設定
app.root_pathを基準にパスを組み立てると、シェルの現在の作業ディレクトリに依存せず、アプリケーションのディレクトリを基準にできます。このローカル向けの例では、app.pyと同じ階層に小さなテキストファイルを作成します。実際に配信するファイルに合わせてパスを置き換えてください。URLやフォームから受け取った任意のファイルシステムパスを、そのままsend_fileに渡してはいけません。
既定では、添付ファイル名はパスから決まるため、この例ではsample.txtとしてダウンロードされます。保存時の名前とダウンロード時の名前を変える場合は、send_fileに意図したdownload_nameの値を渡します。選択するパスは自分で管理し、配信前にファイルが利用可能か確認してください。訪問者が指定したパスより、信頼できるアプリケーション設定から組み立てたパスの方が安全性を判断しやすくなります。
以下をapp.pyとして保存します。アプリケーションと同じ階層にテンプレートとサンプルファイルを作成し、サーバープロセスを起動せずにリクエストを送るため、Flaskのテストクライアントを使います。
このセクションのコードと表示されるラベルや出力は英語のままです。
from pathlib import Path
from flask import Flask, render_template, send_file
app = Flask(__name__)
template_dir = Path(app.root_path) / "templates"
template_dir.mkdir(exist_ok=True)
(template_dir / "index.html").write_text(
'<p><a href="{{ url_for(\'download_file\') }}">Download</a></p>',
encoding="utf-8",
)
SAMPLE_FILE = Path(app.root_path) / "sample.txt"
SAMPLE_FILE.write_text("A file served by Flask.\n", encoding="utf-8")
@app.route("/")
def index():
return render_template("index.html")
@app.route("/download")
def download_file():
return send_file(SAMPLE_FILE, as_attachment=True)
if __name__ == "__main__":
with app.test_client() as client:
print(client.get("/").get_data(as_text=True).strip())
response = client.get("/download")
print(response.status_code)
print(response.headers["Content-Disposition"])
print(response.get_data(as_text=True), end="")
テンプレートはdownload_fileを/downloadに解決し、テストクライアントはそのルートにリクエストします。スクリプトを実行すると、描画されたリンク、レスポンスのステータス、添付ファイルのヘッダー、ファイルの内容が表示されます。添付ファイルのヘッダーがあれば、ブラウザーにレスポンスをダウンロードさせる指定が確認できます。本文の確認では、選択したファイルが配信されたことを確認できます。コード例内のリンク表示は英語の「Download」のままです。
完全な例を実行して確認する
まだインストールしていない場合は、プロジェクトの環境にFlaskをインストールしてから、python app.pyを実行し、描画されたリンクとダウンロードのレスポンスをローカルで確認します。同じルートをブラウザーで試すには、flask --app app runでFlaskの開発用サーバーを起動し、http://127.0.0.1:5000/を開いてDownloadを選択します。テンプレート内の表示ラベル「Download」は英語のままです。ブラウザーはsample.txtをダウンロードするはずです。/downloadに直接アクセスしても、同じ添付ファイルが配信されます。サーバーを停止するにはCtrl+Cを押します。デバッグモードはローカル開発専用です。インターネットからアクセスできるサーバーでは、対話型デバッガーを有効にしないでください。