Streaming file upload using bottle (or flask or similar)

后端 未结 2 1345
囚心锁ツ
囚心锁ツ 2020-12-12 21:15

I have a REST frontend written using Python/Bottle which handles file uploads, usually large ones. The API is wirtten in such a way that:

The client sends PUT with t

相关标签:
2条回答
  • 2020-12-12 21:51

    I recommend splitting the incoming file into smaller-sized chunks on the frontend. I'm doing this to implement a pause/resume function for large file uploads in a Flask application.

    Using Sebastian Tschan's jquery plugin, you can implement chunking by specifying a maxChunkSize when initializing the plugin, as in:

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

    Now the client will send multiple requests when uploading large files. And your server-side code can use the Content-Range header to patch the original large file back together. For a Flask application, the view might look something like:

    # 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,})
    

    For your particular application, you will just have to make sure that the correct auth headers are still sent with each request.

    Hope this helps! I was struggling with this problem for a while ;)

    0 讨论(0)
  • 2020-12-12 22:04

    When using plupload solution might be like this one:

    $("#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'
    });
    

    And your flask-python code in such case would be similar to this:

    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 always sends chunks in exactly same order, from first to last, so you do not have to bother with seek or anything like that.

    0 讨论(0)
提交回复
热议问题