【问题标题】:Streaming file upload using bottle (or flask or similar)使用瓶子(或烧瓶或类似物)上传流式文件
【发布时间】:2013-02-09 01:06:48
【问题描述】:

我有一个使用 Python/Bottle 编写的 REST 前端,用于处理文件上传,通常是大型文件。 API 的编写方式如下:

客户端将 PUT 与文件一起作为有效负载发送。除其他外,它发送 Date 和 Authorization 标头。这是一种针对重放攻击的安全措施——请求使用临时密钥、目标 url、日期和其他一些内容进行签名

现在的问题。如果提供的日期在 15 分钟的给定日期时间窗口内,则服务器接受请求。如果上传需要足够长的时间,它将比允许的时间增量更长。现在,请求授权处理是使用瓶子视图方法上的装饰器完成的。但是,除非上传完成,否则瓶子不会启动调度过程,因此在较长的上传时验证会失败。

我的问题是:有没有办法向瓶子或 WSGI 解释以立即处理请求并在上传时进行流式传输?由于其他原因,这对我也很有用。还是有其他解决方案?在我写这篇文章时,我想到了 WSGI 中间件,但我仍然想要外部洞察力。

我愿意切换到 Flask,甚至其他 Python 框架,因为 REST 前端非常轻量级。

谢谢

【问题讨论】:

  • Plesek,你能分享你的代码吗,请帮助我

标签: python rest file-upload bottle


【解决方案1】:

我建议在前端将传入的文件分割成更小的块。我这样做是为了在 Flask 应用程序中实现大文件上传的暂停/恢复功能。

使用Sebastian Tschan's jquery plugin,可以在初始化插件时通过指定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,})

对于您的特定应用程序,您只需确保仍随每个请求发送正确的身份验证标头。

希望这会有所帮助!我在这个问题上苦苦挣扎了一段时间;)

【讨论】:

  • 我将在某些操作系统(在我的情况下为 Ubuntu 14.10)上添加这一点,如果您执行 open(filename, 'a'),则 seek() 不会移动您的指针。附加将被强制执行,您将始终将传入的块附加到文件的末尾。
  • @petrus-jvrensburg 您的回答非常适合我的需要,但我想知道,如果两个用户同时上传相同的文件名,Flask 怎么能不混合请求?您是否必须实现会话机制来识别两个用户,或者是否有一些底层 http/nginx/uwsgi/flask 属性可以正确地将请求映射到相同的调用方法?感谢您的帮助!
  • @CyrilN。还没有考虑过。但是,如果您已经为您的应用设置了一些身份验证,请使用它。否则,您可以询问“request.remote_addr”和“request.user_agent”来区分同时用户。
  • @petrus-jvrensburg,我无法发送我的文件名,你能显示你的front - end 代码
【解决方案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'
});

在这种情况下,您的烧瓶 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 总是以完全相同的顺序发送块,从头到尾,所以你不必费心寻找或类似的事情。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-03
    • 2020-08-30
    • 2012-12-29
    • 2021-06-09
    • 2021-08-15
    • 2016-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多