Working with File Upload in Flask

Today we will learn file upload with Flask. This tutorial divided into 4 parts that cover the file upload (including image) and validation, setting upload directory path and final step is saving the uploaded files.

Table of content

Create File Upload Form

The very first step is to create an HTML form. We are creating a document upload form to get a better understanding of file upload. The user must provide the information to upload their document i.e. Full Name, Email, Document Attachment, and the Profile Image.

This example also covers the image upload section which is very similar without any hard changes. This is how our form looks.

File Upload with Flask

And take a look at the HTML part for the above view. The main thing to notice in the HTML codes is the Form tag. We added the enctype=”multipart/form-data” property, which is mandatory to upload any file.

<form action="{{ url_for('index') }}" enctype="multipart/form-data" method="POST">
    {{ form.csrf_token }}
    <div class="row">
        <div class="col-6 form-group">
            <label>First Name</label>
            <input type="text" name="first_name" placeholder="First Name" class="form-control">
            {% for error in form.first_name.errors %}
                <small class="text-danger">{{ error }}</small>  
            {% endfor %}
        </div>
        <div class="col-6 form-group">
            <label>Last Name</label>
            <input type="text" name="last_name" placeholder="Last Name" class="form-control">
            {% for error in form.last_name.errors %}
                <small class="text-danger">{{ error }}</small>  
            {% endfor %}
        </div>
        <div class="col-12 form-group">
            <label>Email</label>
            <input type="email" name="email" placeholder="Your Email" class="form-control">
            {% for error in form.email.errors %}
                <small class="text-danger">{{ error }}</small>  
            {% endfor %}
        </div>
        <div class="col-sm-6 form-group">
            <label>Upload Document</label>
            <input type="file" name="document" class="form-control-file">
            {% for error in form.document.errors %}
                <small class="text-danger">{{ error }}</small>  
            {% endfor %}
        </div>
        <div class="col-sm-6 form-group">
            <label>Profile Image</label>
            <input type="file" name="profile" class="form-control-file">
            {% for error in form.profile.errors %}
                <small class="text-danger">{{ error }}</small>  
            {% endfor %}
        </div>
        <div class="col-12 form-group">
            <button class="btn btn-primary">Upload</button>
        </div>
    </div>
</form>

File Upload Validation

The next step is to validate the file upload post request. We have used the WTForms library to validate the form and file upload.

from flask_wtf import Form
from wtforms import StringField
from flask_wtf.file import FileField, FileRequired, FileAllowed
from wtforms.validators import DataRequired, Email

class DocumentUploadForm(Form):
    first_name = StringField('First Name', validators=[DataRequired()])
    last_name = StringField('Last Name', validators=[DataRequired()])
    email = StringField('Email', validators=[DataRequired(), Email()])
    document = FileField('Document', validators=[FileRequired(), FileAllowed(['xls', 'xlsx'], 'Excel Document only!')])
    profile = FileField('Profile', validators=[FileRequired(), FileAllowed(['jpg', 'png'], 'Images only!')])

In this example, we only allowed the XLS and XLSX files as an attachment to upload and JPG and PNG format to upload image.

File upload limit?

You need to add app.config['MAX_CONTENT_LENGTH'] if you need to limit the maximum file size upload.

from flask import Flask
from flask_wtf.csrf import CSRFProtect
import os

csrf = CSRFProtect()

app = Flask('__name__', template_folder="./myapp/templates/")
app.config['SECRET_KEY'] = '325245hkhf486axcv5719bf9397cbn69xv'
app.config['MAX_CONTENT_LENGTH'] = 4 * 1024 * 1024  # 4MB max-limit.
csrf.init_app(app)

Set Upload Directory Path

The next step is to set the path wherever you need to save the profile image and the document. We are going to save the profile image to assets > profile directory and the document file to assets directory.

....
....

@app.route('/', methods = ['GET', 'POST'])
def index():
    form = DocumentUploadForm()
    if form.validate_on_submit():

        assets_dir = os.path.join(
            os.path.dirname(app.instance_path), 'assets'
        )

    return render_template('document_form.html', form=form)

As you see in the above codes, We have set the assets directory path to assets_dir variable.

Save Uploaded Files

The next step is to save the uploaded files to the desired location.

form = DocumentUploadForm()
if form.validate_on_submit():

    assets_dir = os.path.join(
        os.path.dirname(app.instance_path), 'assets'
    )

    d = form.document.data
    f = form.profile.data

    filename = secure_filename(f.filename)
    docname = secure_filename(d.filename)

    # Document and Profile photo save
    f.save(os.path.join(assets_dir, 'profile', filename))
    d.save( os.path.join(assets_dir, docname))

    flash('Document uploaded successfully.')

    return redirect(url_for('index'))

return render_template('document_form.html', form=form)

As the name suggested, the secure_filename function is used to provide a secure version of the file name. Once the file uploaded to the defined directory, we are redirecting the user bach to form with a success flash message.

Posted by Jogesh Sharma

Jogesh Sharma is a web developer and blogger who loves all the things design and the technology, He love all the things having to do with PHP, WordPress, Joomla, Magento, Durpal, Codeigniter, jQuery, HTML5 etc. He is the author of this blog.