python
44 lines · 8 steps
Handling secure file uploads in Flask
A Flask blueprint that validates, renames, and stores uploaded files while rejecting bad or oversized requests.
Explained by
highlit
1import os
2import uuid
3
4from flask import Blueprint, current_app, jsonify, request
5from werkzeug.exceptions import RequestEntityTooLarge
6from werkzeug.utils import secure_filename
7
8uploads = Blueprint("uploads", __name__)
9
10ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif", "pdf"}
11MAX_CONTENT_LENGTH = 8 * 1024 * 1024
12
13
14def _is_allowed(filename):
15 _, _, ext = filename.rpartition(".")
16 return "." in filename and ext.lower() in ALLOWED_EXTENSIONS
17
18
19@uploads.route("/documents", methods=["POST"])
20def upload_document():
21 if "file" not in request.files:
22 return jsonify(error="no file part in request"), 400
23
24 upload = request.files["file"]
25 if not upload.filename:
26 return jsonify(error="no file selected"), 400
27
28 if not _is_allowed(upload.filename):
29 return jsonify(error="unsupported file type"), 415
30
31 safe_name = secure_filename(upload.filename)
32 stored_name = f"{uuid.uuid4().hex}_{safe_name}"
33 dest = os.path.join(current_app.config["UPLOAD_DIR"], stored_name)
34
35 upload.save(dest)
36 size = os.path.getsize(dest)
37
38 return jsonify(filename=stored_name, original=safe_name, size=size), 201
39
40
41@uploads.errorhandler(RequestEntityTooLarge)
42def handle_too_large(err):
43 limit_mb = MAX_CONTENT_LENGTH // (1024 * 1024)
44 return jsonify(error=f"file exceeds {limit_mb}MB limit"), 413
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Validate presence, filename, and type before touching the filesystem to fail fast with meaningful status codes.
- 2Sanitize and randomize stored filenames to avoid path traversal and name collisions.
- 3Register an error handler so framework-level exceptions like oversized bodies return clean JSON.
Related explainers
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/handling-secure-file-uploads-in-flask-explained-python-5f94/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.