Advanced Usage

Parallel Uploads

If you set DROPZONE_UPLOAD_MULTIPLE as True, then you need to save multiple uploads in single request.

However, you can’t get a list of file with request.files.getlist('file'). When you enable parallel upload, Dropzone.js will append a index number after each files, for example: file[2], file[1], file[0]. So, you have to save files like this:

for key, f in request.files.items():
    if key.startswith('file'):
        f.save(os.path.join('the/path/to/save', f.filename))

Here is the full example:

...
app.config['DROPZONE_UPLOAD_MULTIPLE'] = True  # enable parallel upload
app.config['DROPZONE_PARALLEL_UPLOADS'] = 3  # handle 3 file per request

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        for key, f in request.files.items():
            if key.startswith('file'):
                f.save(os.path.join('the/path/to/save', f.filename))

   return 'upload template'

Tip

See examples/parallel-upload for more detail.

CSRF Protect

The CSRF Protect feature was provided by Flask-WTF’s CSRFProtect extension, so you have to install Flask-WTF first:

$ pip install flask-wtf

Then initialize the CSRFProtect:

from flask_wtf.csrf import CSRFProtect

app = Flask(__name__)

# the secret key used to generate CSRF token
app.config['SECRET_KEY'] = 'dev key'
...
# enable CSRF protection
app.config['DROPZONE_ENABLE_CSRF'] = True

csrf = CSRFProtect(app)

Make sure to set the secret key and set DROPZONE_ENABLE_CSRF to True. Now all the upload request will be protected!

We prefer to handle the CSRF error manually, because the error response’s body will be displayed as tooltip below the file thumbnail.

from flask_wtf.csrf import CSRFProtect, CSRFError
...

# handle CSRF error
@app.errorhandler(CSRFError)
def csrf_error(e):
    return e.description, 400

Here I use the e.description as error message, it’s provided by CSRFProtect, one of The CSRF token is missing and The CSRF token is invaild.

Try the demo application in examples/csrf and see CSRFProtect’s documentation for more details.

Content Security Policy

If you like to use your web application under a strict Content Security Policy (CSP), just embedding JavaScript code via {{ dropzone.config() }} into a template will not work. You could move the configuration code into a separate JavaScript file and reference this resource from your HTML page. However, when you like to enable a CSRF protection as well, you need to handle the CSRF token and the CSP nonce value. The simple solution is to embed the configuration code into the HTML page and pass a nonce value for CSP as shown below:

import base64
import os

default_http_header = {'Content-Security-Policy' :
  f"default-src 'self'; script-src 'self' 'nonce-{nonce}'"

nonce = base64.b64encode(os.urandom(64)).decode('utf8')
render_template('template.tmpl', nonce = nonce), 200, default_http_header
{{ dropzone.config(nonce=nonce) }}

Server Side Validation

Although Dropzone.js can handle client side validation for uploads, but you still need to setup server side validation for security concern. Just do what you normally do (extension check, size check etc.), the only thing you should remember is to return plain text error message as response body when something was wrong. Fox example, if we only want user to upload file with .png extension, we can do the validation like this:

@app.route('/', methods=['POST', 'GET'])
def upload():
    if request.method == 'POST':
        f = request.files.get('file')
        if f.filename.split('.')[1] != 'png':
            return 'PNG only!', 400  # return the error message, with a proper 4XX code
        f.save(os.path.join('the/path/to/save', f.filename))
    return render_template('index.html')

The error message will be displayed when you hover the thumbnail for upload file:

error message

error message