13

私は、ファイルのアップロード(通常は大きなもの)を処理するPython/Bottleを使用して記述されたRESTフロントエンドを持っています。APIは、次のように記述されています。

クライアントは、ファイルをペイロードとしてPUTを送信します。特に、DateヘッダーとAuthorizationヘッダーを送信します。これはリプレイ攻撃に対するセキュリティ対策です。リクエストは、ターゲットURL、日付、その他いくつかのものを使用して、一時的なキーで歌われます。

今問題。提供された日付が15分の指定された日時ウィンドウ内にある場合、サーバーは要求を受け入れます。アップロードに十分な時間がかかる場合は、許可された時間デルタよりも長くなります。現在、リクエストの承認処理は、ボトルビューメソッドのデコレータを使用して行われます。ただし、アップロードが終了しない限り、ボトルはディスパッチプロセスを開始しないため、長いアップロードでは検証が失敗します。

私の質問は、リクエストをすぐに処理し、アップロードをストリーミングするようにボトルまたはWSGIに説明する方法はありますか?これは他の理由でも私にとっては役に立ちます。または他の解決策はありますか?これを書いているときに、WSGIミドルウェアが思い浮かびますが、それでも、外部からの洞察が必要です。

RESTフロントエンドは非常に軽量なので、Flaskや他のPythonフレームワークに切り替えてもかまいません。

ありがとうございました

4

2 に答える 2

20

フロントエンドで受信ファイルを小さいサイズのチャンクに分割することをお勧めします。これは、Flaskアプリケーションで大きなファイルをアップロードするための一時停止/再開機能を実装するために行っています。

Sebastian Tschanのjqueryプラグインを使用すると、次maxChunkSizeのように、プラグインを初期化するときにを指定することでチャンクを実装できます。

$('#file-select').fileupload({
    url: '/uploads/',
    sequentialUploads: true,
    done: function (e, data) {
        console.log("uploaded: " + data.files[0].name)
    },
    maxChunkSize: 1000000 // 1 MB
});

これで、クライアントは大きなファイルをアップロードするときに複数のリクエストを送信します。また、サーバー側のコードはContent-Rangeヘッダーを使用して、元の大きなファイルにパッチを適用し直すことができます。Flaskアプリケーションの場合、ビューは次のようになります。

# Upload files
@app.route('/uploads/', methods=['POST'])
def results():

    files = request.files

    # assuming only one file is passed in the request
    key = files.keys()[0]
    value = files[key] # this is a Werkzeug FileStorage object
    filename = value.filename

    if 'Content-Range' in request.headers:
        # extract starting byte from Content-Range header string
        range_str = request.headers['Content-Range']
        start_bytes = int(range_str.split(' ')[1].split('-')[0])

        # append chunk to the file on disk, or create new
        with open(filename, 'a') as f:
            f.seek(start_bytes)
            f.write(value.stream.read())

    else:
        # this is not a chunked request, so just save the whole file
        value.save(filename)

    # send response with appropriate mime type header
    return jsonify({"name": value.filename,
                    "size": os.path.getsize(filename),
                    "url": 'uploads/' + value.filename,
                    "thumbnail_url": None,
                    "delete_url": None,
                    "delete_type": None,})

特定のアプリケーションでは、リクエストごとに正しい認証ヘッダーが送信されていることを確認する必要があります。

お役に立てれば!私はしばらくの間この問題に苦しんでいました;)

于 2013-04-15T15:09:41.800 に答える
2

pluploadソリューションを使用する場合は、次のようになります。

$("#uploader").plupload({
    // General settings
    runtimes : 'html5,flash,silverlight,html4',
    url : "/uploads/",

    // Maximum file size
    max_file_size : '20mb',

    chunk_size: '128kb',

    // Specify what files to browse for
    filters : [
        {title : "Image files", extensions : "jpg,gif,png"},
    ],

    // Enable ability to drag'n'drop files onto the widget (currently only HTML5 supports that)
    dragdrop: true,

    // Views to activate
    views: {
        list: true,
        thumbs: true, // Show thumbs
        active: 'thumbs'
    },

    // Flash settings
    flash_swf_url : '/static/js/plupload-2.1.2/js/plupload/js/Moxie.swf',

    // Silverlight settings
    silverlight_xap_url : '/static/js/plupload-2.1.2/js/plupload/js/Moxie.xap'
});

そして、そのような場合のあなたのflask-pythonコードはこれに似ています:

from werkzeug import secure_filename

# Upload files
@app.route('/uploads/', methods=['POST'])
def results():
    content = request.files['file'].read()
    filename = secure_filename(request.values['name'])

    with open(filename, 'ab+') as fp:
        fp.write(content)

    # send response with appropriate mime type header
    return jsonify({
        "name": filename,
        "size": os.path.getsize(filename),
        "url": 'uploads/' + filename,})

Pluploadは常にチャンクを最初から最後までまったく同じ順序で送信するため、seekなどを気にする必要はありません。

于 2015-03-24T17:35:32.460 に答える