Flask File Uploads

Flask handles file uploads through a special form encoding type and the request.files object. Users select a file from their computer, the browser sends it to the server, and Flask saves it to disk.

Setting Up the Form for File Upload

An HTML form that uploads files must use enctype="multipart/form-data". Without this attribute, the browser sends only the file name, not the file content.

<form method="POST" enctype="multipart/form-data">
  <label>Choose a file:</label>
  <input type="file" name="photo">
  <button type="submit">Upload</button>
</form>

Receiving the File in Flask

import os
from flask import Flask, request, redirect, url_for

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'static/uploads'

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        file = request.files.get('photo')
        if file and file.filename:
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], file.filename))
            return 'File uploaded successfully!'
    return '''
        <form method="POST" enctype="multipart/form-data">
            <input type="file" name="photo">
            <button type="submit">Upload</button>
        </form>
    '''

The Upload Process Diagram

Browser:
  User picks file → form submitted with enctype=multipart/form-data

Flask:
  request.files['photo'] → FileStorage object
        │
  file.filename  → 'profile.jpg'
  file.read()    → raw bytes
  file.save(path)→ writes to disk

Validating File Type with Werkzeug

Never trust the filename a user provides. A malicious user might upload a script with a .jpg extension. Check the file extension against an allowed list:

ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'png', 'gif', 'pdf'}

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

Securing the Filename

Werkzeug's secure_filename() sanitizes the filename to remove dangerous characters like ../ that could let a user write files outside the upload folder.

from werkzeug.utils import secure_filename
import os

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        file = request.files.get('photo')
        if not file or not file.filename:
            return 'No file selected.', 400

        if not allowed_file(file.filename):
            return 'File type not allowed.', 400

        filename = secure_filename(file.filename)
        save_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
        file.save(save_path)
        return f'Uploaded: {filename}'
    return render_template('upload.html')

secure_filename in Action

Original FilenameAfter secure_filename()
../../etc/passwdetc_passwd
my photo 2024.jpgmy_photo_2024.jpg
résumé.pdfresume.pdf
script.js.jpgscript.js.jpg (still check extension!)

Generating Unique Filenames

Two users might upload files with the same name. The second upload overwrites the first. Use Python's uuid module to generate unique filenames:

import uuid

def unique_filename(original):
    ext = original.rsplit('.', 1)[1].lower()
    return f'{uuid.uuid4().hex}.{ext}'

filename = unique_filename(secure_filename(file.filename))
# Example: 'a3f2c91b4d8e7654abc123.jpg'

Setting a Maximum File Size

Limit file size to prevent users from uploading huge files that fill your disk or slow your server:

app.config['MAX_CONTENT_LENGTH'] = 5 * 1024 * 1024  # 5 MB limit

When a user uploads a file larger than this limit, Flask returns HTTP 413 Request Entity Too Large automatically.

Displaying the Uploaded File

Files saved inside the static folder are directly accessible by the browser:

@app.route('/upload', methods=['POST'])
def upload():
    file = request.files.get('photo')
    filename = secure_filename(file.filename)
    file.save(os.path.join('static/uploads', filename))
    return render_template('show.html', filename=filename)
{# show.html #}
<img src="{{ url_for('static', filename='uploads/' + filename) }}" alt="Uploaded photo">

Summary

File uploads require enctype="multipart/form-data" on the HTML form. Flask reads the uploaded file from request.files. Always validate the file extension against an allowed list, sanitize the filename with secure_filename(), and set a maximum file size. Store files in a dedicated upload folder within static/ to serve them back to users via a URL.

Leave a Comment

Your email address will not be published. Required fields are marked *