Upload multiple files using "drag and drop" with html5 and flask

In the aritcle How to upload multiple files with python flask, we showed how to upload with html form.

In this aritcle, we will show how to upload in the "drag and drop" way. In this way, we can also show progress and speed during uploading.

Assumption

In the following sections, we aussume that the url /upload handles uploading.

You may need to change /upload to your own url.

html

<div id="drop_area" style="padding:100px; border: 1px solid black">
    Drag and drop your files here to upload.
</div>
<div id="upload_progress"></div>
<div id="speed"></div>
  • drop_area will be used to receive files.
  • upload_progress will be used to show uploading progress.
  • speed will be used to show uploading speed.

javascript

// prevent the default behavior of web browser
['dragleave', 'drop', 'dragenter', 'dragover'].forEach(function (evt) {
    document.addEventListener(evt, function (e) {
        e.preventDefault();
    }, false);
});

var drop_area = document.getElementById('drop_area');
drop_area.addEventListener('drop', function (e) {
    e.preventDefault();
    var fileList = e.dataTransfer.files; // the files to be uploaded
    if (fileList.length == 0) {
        return false;
    }

    // we use XMLHttpRequest here instead of fetch, because with the former we can easily implement progress and speed.
    var xhr = new XMLHttpRequest();
    xhr.open('post', '/upload', true); // aussume that the url /upload handles uploading.
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4 && xhr.status == 200) {
            // uploading is successful
            alert('Successfully uploaded!');  // please replace with your own logic
        }
    };

    // show uploading progress
    var lastTime = Date.now();
    var lastLoad = 0;
    xhr.upload.onprogress = function (event) {
        if (event.lengthComputable) {
            // update progress
            var percent = Math.floor(event.loaded / event.total * 100);
            document.getElementById('upload_progress').textContent = percent + '%';

            // update speed
            var curTime = Date.now();
            var curLoad = event.loaded;
            var speed = ((curLoad - lastLoad) / (curTime - lastTime) / 1024).toFixed(2);
            document.getElementById('speed').textContent = speed + 'MB/s'
            lastTime = curTime;
            lastLoad = curLoad;
        }
    };

    // send files to server
    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    var fd = new FormData();
    for (let file of fileList) {
        fd.append('files', file);
    }
    lastTime = Date.now();
    xhr.send(fd);
}, false);

Backend flask

import os
from flask import request
from werkzeug.utils import secure_filename

@app.route('/upload', methods=('POST',))
def upload():
    files = request.files.getlist('files')
    for file in files:
        fn = secure_filename(file.filename)
        file.save(os.path.join(FILES_DIR, fn))  # replace FILES_DIR with your own directory
    return 'ok'  # change to your own logic

Things you should notice about the flask code above:

  • methods must include POST
  • To get files, you must use request.files.getlist('files') instead of request.files['files'], because the latter is only used when uploading a single file.
  • use secure_filename to prevent malicious file name.
Posted on 2022-04-01