cft

How To Send Form Data With Files Using Python’s Flask and Dropzone.js

Pass multiple files with a web application


user

Erikka Innes

3 years ago | 6 min read

Dropzone.js is an incredibly cool JavaScript library that lets you upload files immediately by dragging a file to a zone and dropping it. Then Dropzone does the rest, sending your file off to be saved wherever you specify. You can drop multiple files or a single file. Flask is a WSGI web application framework that makes building web applications easy. There is a fantastic tutorial if you want to use basic Dropzone called “Handling File Uploads With Flask.”

However, what if you want to add your Dropzone to an existing form and receive both files and form data with your Flask backend? This is a question I saw asked more than a few times online, so here’s a simple way to use Flask with Dropzone to send multiple files and form data with the click of a “Submit” button.

Why Would You Want To Send Files and Form Data Together?

There are a few reasons you might want to send files and form data together:

  • If you want to send the files you’re collecting to an API, you might need to collect an API key, token, or something similar in order to complete an upload.
  • If you want to associate the files with a person or data about the file, you might want to collect form data to do that.
  • If you want to allow people to alter information about their file while sending. For example, maybe you want to offer them the chance to change the name of a file as they send it.

Prerequisites

You’ll need to have a few things to get started:

  • Python installed.
  • Virtual environment for your project (optional but recommended).
  • Flask installed.
  • Requests installed.
  • See the project on GitHub.

Note: This project is based on Miguel Grinberg’s Flask and Dropzone tutorial.

What’s in the HTML File?

For the sake of simplicity, I’ve created a simple form. Let’s go through the different parts of the HTML.

The form

<form action="/" class="dropzone" id="my-dropzone" method="POST">      
<div class="form-group">
<button type="submit">Submit</button>
</div>
<div class="form-group">
<label for="myFormData">Example Form Data</label>
<input type="text" name="myFormData" id="myFormData" placeholder="Enter your data" class="form-control" required/> </div>
</form>

I’ve included this snippet for reference. If you want to see everything together, please grab the code sample out of GitHub. You can see between the body tags that the form is doing a few things:

  1. form action=“/” tells you where you want the information sent when you click the “Submit” button. You can include this information through a Dropzone configuration as well. I’m describing it here because an easy mistake is assuming Dropzone automatically knows where to send everything. It does if you specify using either Dropzone or, as I’ve selected, with the form action.
  2. class=“dropzone” is how you let Dropzone know where you want to place a zone.
  3. id=“my-dropzone” is what you need to reference the form and add your configuration options for Dropzone.
  4. method=“POST” makes it clear you want to POST the form data and file. If you leave this off, it will try to send your information with GET, meaning the file will be left out.

The rest of the form is just setting up the “Submit” button and the fields you wanted. If you are wondering where the multipart setting is, you don’t need it here because you’re using Dropzone. Depending on what you decide to create, you may need to add it.

Dependencies

Up top between the head tags, you can see all the different scripts and stylesheets you’ll need. Dropzone is flexible, so if you want to do things differently you can. You can also put the Dropzone .js and .css files into a static folder for Flask to find them. I opted to use CDNs for them instead.

<script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.1/min/dropzone.min.js">
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.7.1/min/dropzone.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js">
</script>

Dropzone configuration script

You can place the configuration script for Dropzone in a few places, including in a separate file that you reference. I opted to add it under the dependencies in the HTML file using script tags. The configuration looks like this:

To set up, you have myDropzone. This is a reference to your form, where you put the id as “my-dropzone.” Whatever name you choose, you put the name in camel case based on where you placed the dashes. So if you have “my-amazing-dropzone,” it would be myAmazingDropzone. For the options, you set these:

  • autoProcessQueue to false. This tells Dropzone not to automatically grab your files and send them away.
  • uploadMultiple to true. If you want to be able to drag-and-drop a bunch of files at once, you need this set to true.
  • parallelUploads to 10 because I think testing it out with ten is probably enough for this walkthrough. This is how many files will try to upload at the same time.
  • maxFiles is for how many files you want someone to be able to drop at once. I put 10 again.

Next, we add an event listener to our “Submit” button. We can reference it by the type of button it is, so an ID isn’t needed. Then we tell Dropzone to upload everything when the “Submit” button is clicked. That’s it for the form. Let’s go take a look at app.py next.

What’s in the app.py File?

The code for app.py looks like this (for the full code, please go to the sample on GitHub):

from flask import Flask, render_template, request, redirect, url_for, abort
import os
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config['UPLOAD_EXTENSIONS'] = ['.mov', '.mp4', '.m4v', '.jpm', '.jp2', '.3gp', '.3g2', '.mkv', '.mpg', '.ogv', '.webm', '.wmv' ]
app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024
@app.errorhandler(413)
def too_large(e):
return "File is too large", 413
@app.route('/')
def index():
return render_template('index.html')
@app.route('/', methods=['POST'])
def upload_file():
my_files = request.files
formData = request.form['myFormData']

with open("my_data.txt", "x+") as my_data:
my_data.write(formData)

for item in my_files:
uploaded_file = my_files.get(item)
uploaded_file.filename = secure_filename(uploaded_file.filename)
if uploaded_file.filename != '':
file_ext = os.path.splitext(uploaded_file.filename)[1]
if file_ext not in app.config['UPLOAD_EXTENSIONS']:
abort(400)

uploaded_file.save(uploaded_file.filename)

return redirect(url_for('index'))

You can read a detailed discussion about this code in Grinberg’s tutorial. The changes I made here were:

  • Making this sample about video. For upload extensions, I only made it possible to upload videos. You can change that to be for text files or images. Or if you aren’t worried about securing your files at all, you can get rid of the upload extensions configuration.
  • Making it possible to process form data alongside files. The important thing to note is that your files and forms are in two different parts of the request information. request.form is going to contain all the fields you added, and request.files will have your file information. Files are in an immutable multi-dictionary.
  • Keeping the files object. Instead of taking the filename out of the item in my_files, you can see I just update the filename in the item.
  • Adding a spot where I take the form data and write it to a text file. You can do lots of other things like add a database or process this information some other way. This was just to provide a simple example. By the way, all the files are stored in the same directory in this example.

You may want to go over Grinberg’s tutorial if you’re handling images and want thumbnails, as he presents a way to do that. It’s also a useful tutorial if you want to read about the various security features in the code snippet.

Depending on the size of your files, you might want to change the MAX_CONTENT_LENGTH configuration to accept bigger files instead of just 1024 * 1024.

You can also find more Dropzone information on the Dropzone.js site. I hope this helped you get started with using Dropzone with multiple files and form data!

Upvote


user
Created by

Erikka Innes

Developer Advocate and Comedian


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles