Flask File Uploads & Caching

πŸ“Ž Basic upload

<form method="POST" enctype="multipart/form-data">   <!-- required for file uploads -->
    <input type="file" name="photo">
    <button type="submit">Upload</button>
</form>
from werkzeug.utils import secure_filename
 
@app.route("/upload", methods=["POST"])
def upload():
    if "photo" not in request.files:
        return "No file part", 400
    file = request.files["photo"]
    if file.filename == "":
        return "No file selected", 400
 
    filename = secure_filename(file.filename)   # sanitize β€” prevents path traversal
    file.save(f"uploads/{filename}")
    return "Uploaded"

Never save file.filename raw. Always run through secure_filename().

βœ… Validate type + size

ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg", "gif"}
 
def allowed_file(filename):
    return "." in filename and filename.rsplit(".",1)[1].lower() in ALLOWED_EXTENSIONS
 
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024   # 16 MB cap β†’ auto 413 if exceeded

πŸ—‚οΈ Upload folder + serving back

import os
app.config["UPLOAD_FOLDER"] = os.path.join(app.root_path, "uploads")
 
from flask import send_from_directory
 
@app.route("/uploads/<filename>")
def uploaded_file(filename):
    return send_from_directory(app.config["UPLOAD_FOLDER"], filename)

send_from_directory has built-in path traversal protection + correct content-type headers.

⚑ Caching (Flask-Caching)

pip install flask-caching
from flask_caching import Cache
app.config["CACHE_TYPE"] = "SimpleCache"   # in-memory, dev only
cache = Cache(app)

Cache a whole view

@app.route("/expensive-report")
@cache.cached(timeout=300)   # 5 min
def expensive_report():
    data = run_expensive_query()
    return render_template("report.html", data=data)

Cache a specific value

def get_top_products():
    result = cache.get("top_products")
    if result is None:
        result = run_expensive_query()
        cache.set("top_products", result, timeout=600)
    return result

Invalidation

cache.delete("top_products")
cache.clear()

Production backend

app.config["CACHE_TYPE"] = "RedisCache"
app.config["CACHE_REDIS_URL"] = "redis://localhost:6379/0"

SimpleCache doesn't work across multiple worker processes β€” use Redis in production.

πŸ”— Next

Flask Testing & Deployment