CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-flask-wtf

Form rendering, validation, and CSRF protection for Flask with WTForms.

72

0.91x
Overview
Eval results
Files

file-upload.mddocs/

File Upload

Secure file upload handling with Werkzeug integration, providing specialized file fields and comprehensive validation including file type restrictions, size limits, and presence checking.

Capabilities

File Fields

Werkzeug-aware file upload fields that properly handle FileStorage objects from Flask requests.

class FileField:
    """
    Werkzeug-aware file upload field.
    Processes FileStorage objects from Flask request.files.
    """
    def process_formdata(self, valuelist):
        """Process form data containing FileStorage objects"""

class MultipleFileField:
    """
    Multiple file upload field supporting multiple FileStorage objects.
    
    Added in version 1.2.0
    """
    def process_formdata(self, valuelist):
        """Process form data containing multiple FileStorage objects"""

File Validators

Comprehensive validation for uploaded files including presence, type restrictions, and size limits.

class FileRequired:
    """
    Validates that uploaded file(s) are present and valid FileStorage objects.
    
    Args:
        message: Custom error message
    """
    def __init__(self, message=None): ...
    def __call__(self, form, field): ...

class FileAllowed:
    """
    Validates that uploaded file(s) have allowed extensions.
    
    Args:
        upload_set: List of allowed extensions or Flask-Uploads UploadSet
        message: Custom error message
    """
    def __init__(self, upload_set, message=None): ...
    def __call__(self, form, field): ...

class FileSize:
    """
    Validates that uploaded file(s) are within size limits.
    
    Args:
        max_size: Maximum file size in bytes
        min_size: Minimum file size in bytes (default: 0)
        message: Custom error message
    """
    def __init__(self, max_size, min_size=0, message=None): ...
    def __call__(self, form, field): ...

Validator Aliases

Convenient lowercase aliases for file validators.

file_required = FileRequired
file_allowed = FileAllowed
file_size = FileSize

Usage Examples

Basic File Upload

from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired, FileAllowed
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

class UploadForm(FlaskForm):
    title = StringField('Title', validators=[DataRequired()])
    file = FileField('File', validators=[
        FileRequired(),
        FileAllowed(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'], 'Images and documents only!')
    ])
    submit = SubmitField('Upload')

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    form = UploadForm()
    
    if form.validate_on_submit():
        title = form.title.data
        file = form.file.data  # Werkzeug FileStorage object
        
        if file:
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            flash(f'File {filename} uploaded successfully!')
            return redirect(url_for('upload_file'))
    
    return render_template('upload.html', form=form)

Multiple File Upload

from flask_wtf.file import MultipleFileField, FileAllowed, FileSize

class MultiUploadForm(FlaskForm):
    files = MultipleFileField('Files', validators=[
        FileAllowed(['jpg', 'png', 'gif'], 'Images only!'),
        FileSize(max_size=5*1024*1024, message='Files must be smaller than 5MB')  # 5MB limit
    ])
    submit = SubmitField('Upload All')

@app.route('/multi-upload', methods=['GET', 'POST'])
def multi_upload():
    form = MultiUploadForm()
    
    if form.validate_on_submit():
        files = form.files.data  # List of FileStorage objects
        
        uploaded_files = []
        for file in files:
            if file:
                filename = secure_filename(file.filename)
                file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
                uploaded_files.append(filename)
        
        flash(f'Uploaded {len(uploaded_files)} files: {", ".join(uploaded_files)}')
        return redirect(url_for('multi_upload'))
    
    return render_template('multi_upload.html', form=form)

Advanced File Validation

class AdvancedUploadForm(FlaskForm):
    # Required file with extension and size validation
    document = FileField('Document', validators=[
        FileRequired('Please select a file'),
        FileAllowed(['pdf', 'doc', 'docx'], 'Documents only'),
        FileSize(max_size=10*1024*1024, min_size=1024, 
                message='File must be between 1KB and 10MB')
    ])
    
    # Optional image with validation
    image = FileField('Image', validators=[
        FileAllowed(['jpg', 'jpeg', 'png', 'gif'], 'Images only'),
        FileSize(max_size=2*1024*1024, message='Image must be smaller than 2MB')
    ])
    
    submit = SubmitField('Upload')

# Using validator aliases
class SimpleUploadForm(FlaskForm):
    file = FileField('File', validators=[
        file_required('File is required'),
        file_allowed(['txt', 'csv'], 'Text files only'),
        file_size(max_size=1024*1024, message='File too large')  # 1MB
    ])

Flask-Uploads Integration

Flask-WTF integrates with Flask-Uploads for organized file handling:

from flask_uploads import UploadSet, configure_uploads, IMAGES, DOCUMENTS

# Configure upload sets
photos = UploadSet('photos', IMAGES)
documents = UploadSet('documents', DOCUMENTS)
configure_uploads(app, (photos, documents))

class UploadsForm(FlaskForm):
    # Using UploadSet with FileAllowed
    photo = FileField('Photo', validators=[
        FileRequired(),
        FileAllowed(photos, 'Images only!')  # Uses UploadSet
    ])
    
    document = FileField('Document', validators=[
        FileAllowed(documents, 'Documents only!')  # Uses UploadSet
    ])
    
    submit = SubmitField('Upload')

Custom File Validation

from wtforms import ValidationError
import imghdr

class ImageUploadForm(FlaskForm):
    image = FileField('Image', validators=[FileRequired()])
    
    def validate_image(self, field):
        """Custom validator to check if file is a valid image"""
        if field.data:
            # Check file header to verify it's actually an image
            field.data.seek(0)  # Reset file pointer
            if not imghdr.what(field.data):
                raise ValidationError('File is not a valid image')
            field.data.seek(0)  # Reset for saving
            
            # Check image dimensions
            from PIL import Image
            try:
                img = Image.open(field.data)
                width, height = img.size
                if width > 2000 or height > 2000:
                    raise ValidationError('Image dimensions too large (max 2000x2000)')
                field.data.seek(0)  # Reset for saving
            except Exception:
                raise ValidationError('Cannot process image file')

File Processing Examples

import os
from werkzeug.utils import secure_filename

@app.route('/process-upload', methods=['GET', 'POST'])
def process_upload():
    form = UploadForm()
    
    if form.validate_on_submit():
        file = form.file.data
        
        # File information
        filename = file.filename
        content_type = file.content_type
        content_length = file.content_length
        
        # Secure the filename
        safe_filename = secure_filename(filename)
        
        # Create unique filename to prevent conflicts
        timestamp = int(time.time())
        unique_filename = f"{timestamp}_{safe_filename}"
        
        # Save file
        file_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)
        file.save(file_path)
        
        # Process file content
        if content_type.startswith('text/'):
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
                # Process text content
        elif content_type.startswith('image/'):
            from PIL import Image
            img = Image.open(file_path)
            # Process image
        
        return {'status': 'success', 'filename': unique_filename}
    
    return render_template('upload.html', form=form)

Template Examples

Single File Upload Template

<form method="POST" enctype="multipart/form-data">
    {{ form.hidden_tag() }}
    
    <div class="form-group">
        {{ form.title.label(class="form-label") }}
        {{ form.title(class="form-control") }}
        {% for error in form.title.errors %}
            <div class="text-danger">{{ error }}</div>
        {% endfor %}
    </div>
    
    <div class="form-group">
        {{ form.file.label(class="form-label") }}
        {{ form.file(class="form-control", accept=".txt,.pdf,.png,.jpg,.jpeg,.gif") }}
        {% for error in form.file.errors %}
            <div class="text-danger">{{ error }}</div>
        {% endfor %}
    </div>
    
    {{ form.submit(class="btn btn-primary") }}
</form>

Multiple File Upload Template

<form method="POST" enctype="multipart/form-data">
    {{ form.hidden_tag() }}
    
    <div class="form-group">
        {{ form.files.label(class="form-label") }}
        {{ form.files(class="form-control", multiple=true, accept="image/*") }}
        <small class="form-text text-muted">
            Select multiple image files (max 5MB each)
        </small>
        {% for error in form.files.errors %}
            <div class="text-danger">{{ error }}</div>
        {% endfor %}
    </div>
    
    {{ form.submit(class="btn btn-primary") }}
</form>

Configuration

Upload Settings

import os

# Basic upload configuration
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024  # 16MB max request size

# Ensure upload directory exists
os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

Security Considerations

  1. Always use secure_filename() to sanitize uploaded filenames
  2. Validate file types using both extension and content checking
  3. Set reasonable size limits to prevent resource exhaustion
  4. Store uploads outside the web root when possible
  5. Scan uploads for viruses in production environments
  6. Use unique filenames to prevent conflicts and enumeration

Install with Tessl CLI

npx tessl i tessl/pypi-flask-wtf

docs

csrf-protection.md

file-upload.md

form-handling.md

index.md

recaptcha.md

tile.json