ファイルのアップロード—Flaskドキュメント

提供:Dev Guides
< FlaskFlask/docs/2.0.x/patterns/fileuploads
移動先:案内検索

ファイルのアップロード

はい、ファイルアップロードの古き良き問題です。 ファイルのアップロードの基本的な考え方は、実際には非常に単純です。 基本的には次のように機能します。

  1. <form>タグはenctype=multipart/form-dataでマークされ、<input type=file>はその形式で配置されます。
  2. アプリケーションは、リクエストオブジェクトのfilesディクショナリからファイルにアクセスします。
  3. ファイルのsave()メソッドを使用して、ファイルをファイルシステムのどこかに永続的に保存します。

優しい紹介

特定のアップロードフォルダにファイルをアップロードし、ユーザーにファイルを表示する非常に基本的なアプリケーションから始めましょう。 アプリケーションのブートストラップコードを見てみましょう。

import os
from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename

UPLOAD_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'}

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

したがって、最初にいくつかのインポートが必要です。 ほとんどは簡単なはずです。werkzeug.secure_filename()については少し後で説明します。 UPLOAD_FOLDERはアップロードされたファイルを保存する場所であり、ALLOWED_EXTENSIONSは許可されたファイル拡張子のセットです。

許可される拡張機能を制限するのはなぜですか? サーバーがクライアントにデータを直接送信している場合は、ユーザーがそこにすべてをアップロードできないようにする必要があります。 これにより、ユーザーがXSSの問題を引き起こす可能性のあるHTMLファイルをアップロードできないようにすることができます(クロスサイトスクリプティング(XSS)を参照)。 また、サーバーが.phpファイルを実行する場合は、必ず許可しないようにしてください。ただし、サーバーにPHPがインストールされているのは誰ですか。 :)

次に、拡張子が有効かどうかをチェックし、ファイルをアップロードして、アップロードされたファイルのURLにユーザーをリダイレクトする関数は次のとおりです。

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        file = request.files['file']
        # If the user does not select a file, the browser submits an
        # empty file without a filename.
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return redirect(url_for('download_file', name=filename))
    return '''
    <!doctype html>
    <title>Upload new File</title>
    <h1>Upload new File</h1>
    <form method=post enctype=multipart/form-data>
      <input type=file name=file>
      <input type=submit value=Upload>
    </form>
    '''

では、そのsecure_filename()関数は実際に何をするのでしょうか? ここで問題となるのは、「ユーザー入力を絶対に信用しない」という原則があるということです。 これは、アップロードされたファイルのファイル名にも当てはまります。 送信されたすべてのフォームデータは偽造される可能性があり、ファイル名は危険な場合があります。 今のところ覚えておいてください。ファイルシステムに直接保存する前に、常にその関数を使用してファイル名を保護してください。

プロのための情報

それで、あなたはそのsecure_filename()関数が何をするのか、そしてあなたがそれを使わない場合の問題は何であるのかに興味がありますか? したがって、誰かが次の情報を filename としてアプリケーションに送信すると想像してみてください。

filename = "../../../../home/username/.bashrc"

../の数が正しく、これをUPLOAD_FOLDERと結合すると、ユーザーはサーバーのファイルシステム上のファイルを変更できない可能性があります。 これには、アプリケーションがどのように見えるかについての知識が必要ですが、私を信じてください、ハッカーは忍耐強いです:)

次に、その関数がどのように機能するかを見てみましょう。

>>> secure_filename('../../../../home/username/.bashrc')
'home_username_.bashrc'

アップロードされたファイルをユーザーがダウンロードできるように提供できるようにしたいと考えています。 download_fileビューを定義して、アップロードフォルダー内のファイルを名前で提供します。 url_for("download_file", name=name)はダウンロードURLを生成します。

from flask import send_from_directory

@app.route('/uploads/<name>')
def download_file(name):
    return send_from_directory(app.config["UPLOAD_FOLDER"], name)

ミドルウェアまたはHTTPサーバーを使用してファイルを提供している場合は、download_fileエンドポイントをbuild_onlyとして登録して、url_forが表示機能なしで機能するようにすることができます。

app.add_url_rule(
    "/uploads/<name>", endpoint="download_file", build_only=True
)

アップロードの改善

バージョン0.6の新機能。


では、Flaskはアップロードをどのように正確に処理しますか? ファイルが適度に小さい場合はWebサーバーのメモリに保存され、そうでない場合は一時的な場所に保存されます(tempfile.gettempdir()によって返されます)。 しかし、アップロードが中止されるまでの最大ファイルサイズをどのように指定しますか? デフォルトでは、Flaskは無制限のメモリ量でファイルのアップロードを受け入れますが、MAX_CONTENT_LENGTH構成キーを設定することでそれを制限できます。

from flask import Flask, Request

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000

上記のコードは、最大許容ペイロードを16メガバイトに制限します。 より大きなファイルが送信されると、FlaskはRequestEntityTooLarge例外を発生させます。

接続リセットの問題

ローカル開発サーバーを使用すると、413応答ではなく接続リセットエラーが発生する場合があります。 本番WSGIサーバーでアプリを実行すると、正しいステータス応答が得られます。


この機能はFlask0.6で追加されましたが、リクエストオブジェクトをサブクラス化することで、古いバージョンでも実現できます。 詳細については、ファイル処理に関するWerkzeugのドキュメントを参照してください。


プログレスバーをアップロードする

しばらく前、多くの開発者は、受信ファイルを小さなチャンクで読み取り、アップロードの進行状況をデータベースに保存して、クライアントからJavaScriptを使用して進行状況をポーリングできるようにすることを考えていました。 クライアントは5秒ごとにサーバーに送信量を尋ねますが、これはすでに知っておくべきことです。


より簡単なソリューション

現在、より高速に動作し、より信頼性の高い、より優れたソリューションがあります。 プログレスバーの作成を容易にするフォームプラグインを備えた jQuery のようなJavaScriptライブラリがあります。

ファイルアップロードの共通パターンは、アップロードを処理するすべてのアプリケーションでほとんど変更されていないため、アップロードを許可するファイル拡張子を制御できる本格的なアップロードメカニズムを実装するFlask拡張機能もいくつかあります。