π Flask File Handling: Your Digital Mailroom
Imagine you run a magical mailroom. People send you packages (files), and your job is to store them safely and deliver them back when asked. Flaskβs file handling is exactly like this!
π― What Weβll Learn
Think of Flask as your helpful assistant who knows how to:
- Deliver files to visitors (
send_file) - Fetch files from specific rooms (
send_from_directory) - Clean up messy package labels (secure filename)
- Organize storage rooms (file storage paths)
- Hand back stored packages (serving uploaded files)
π¬ The send_file Function
What Is It?
send_file is like a personal delivery person. You tell them: βGo get this exact file and hand it to the visitor.β
Simple Example
from flask import send_file
@app.route('/download')
def download_report():
return send_file(
'reports/sales.pdf',
as_attachment=True
)
Whatβs Happening?
graph TD A["User clicks Download"] --> B["Flask gets request"] B --> C["send_file finds the file"] C --> D[File sent to user's browser] D --> E["Download starts!"]
Key Options
| Option | What It Does | Example |
|---|---|---|
as_attachment=True |
Forces download dialog | User saves the file |
download_name |
Changes the filename | download_name='my_report.pdf' |
mimetype |
Tells browser file type | mimetype='image/png' |
Real-Life Use
@app.route('/invoice/<int:id>')
def get_invoice(id):
path = f'invoices/inv_{id}.pdf'
return send_file(
path,
as_attachment=True,
download_name=f'Invoice_{id}.pdf'
)
ποΈ The send_from_directory Function
Why Is This Different?
Imagine your mailroom has many rooms (folders). send_from_directory says: βGo to THIS specific room and find THIS file.β
Why use it? Itβs safer! It checks that no one tricks you into leaving that room.
Simple Example
from flask import send_from_directory
@app.route('/uploads/<filename>')
def serve_upload(filename):
return send_from_directory(
'uploads',
filename
)
The Safety Shield
graph TD A["User asks for: ../../secrets.txt"] --> B["send_from_directory checks"] B --> C{Is this safe?} C -->|NO - Bad path!| D["β Blocked"] C -->|YES - Safe file| E["β File sent"]
Example with Options
@app.route('/photos/<name>')
def get_photo(name):
return send_from_directory(
app.config['PHOTO_FOLDER'],
name,
as_attachment=False
)
π‘ Pro Tip: Use
send_from_directorywhen serving user uploads. It prevents sneaky path attacks!
π‘οΈ Secure Filename Handling
The Problem
What if someone uploads a file called ../../../etc/password? Thatβs trying to escape your upload folder and access system files! Scary!
The Solution: secure_filename
from werkzeug.utils import secure_filename
bad_name = "../../../evil.txt"
safe_name = secure_filename(bad_name)
# Result: "evil.txt"
What It Fixes
| Dangerous Input | After secure_filename |
|---|---|
../../../file.txt |
file.txt |
my file.pdf |
my_file.pdf |
<script>bad.js |
scriptbad.js |
ζ₯ζ¬θͺ.png |
_.png |
Complete Upload Example
from werkzeug.utils import secure_filename
import os
ALLOWED = {'png', 'jpg', 'pdf'}
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() \
in ALLOWED
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['document']
if file and allowed_file(file.filename):
safe = secure_filename(file.filename)
file.save(
os.path.join(
app.config['UPLOAD_FOLDER'],
safe
)
)
return 'Uploaded!'
return 'Invalid file', 400
π File Storage Paths
Why Paths Matter
Your mailroom needs organized storage rooms. Wrong paths = lost files!
Setting Up Your Storage
import os
# Get your app's home directory
BASE_DIR = os.path.dirname(
os.path.abspath(__file__)
)
# Create upload folder path
UPLOAD_FOLDER = os.path.join(
BASE_DIR,
'uploads'
)
# Tell Flask about it
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
# Make sure folder exists!
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
The Path Building Blocks
graph TD A["BASE_DIR"] --> B["/home/app/myproject"] B --> C["+ &#39;uploads&#39;"] C --> D["/home/app/myproject/uploads"] D --> E["+ &#39;photo.jpg&#39;"] E --> F["/home/app/myproject/uploads/photo.jpg"]
Common Path Patterns
# For uploads
upload_path = os.path.join(
app.config['UPLOAD_FOLDER'],
filename
)
# For static files
static_path = os.path.join(
app.static_folder,
'images',
'logo.png'
)
# Check if file exists
if os.path.exists(upload_path):
return send_file(upload_path)
π Serving Uploaded Files
The Complete Picture
Now letβs put it all together! Users upload files, you store them safely, then serve them back.
Complete Working Example
from flask import Flask, request
from flask import send_from_directory
from werkzeug.utils import secure_filename
import os
app = Flask(__name__)
# Configuration
app.config['UPLOAD_FOLDER'] = 'uploads'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max
ALLOWED = {'png', 'jpg', 'gif', 'pdf'}
def allowed_file(name):
return '.' in name and \
name.rsplit('.', 1)[1].lower() \
in ALLOWED
# Upload endpoint
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return 'No file!', 400
file = request.files['file']
if file.filename == '':
return 'No selected file', 400
if file and allowed_file(file.filename):
safe_name = secure_filename(
file.filename
)
file.save(
os.path.join(
app.config['UPLOAD_FOLDER'],
safe_name
)
)
return f'Saved as {safe_name}'
return 'File type not allowed', 400
# Serve endpoint
@app.route('/files/<name>')
def serve_file(name):
return send_from_directory(
app.config['UPLOAD_FOLDER'],
name
)
The Complete Flow
graph TD A["π€ User selects file"] --> B["π€ Upload request"] B --> C["π Check file type"] C --> D["π‘οΈ Secure filename"] D --> E["πΎ Save to uploads/"] E --> F["β Return success"] G["π€ User wants file"] --> H["π₯ Request /files/name"] H --> I["π send_from_directory"] I --> J["π Find in uploads/"] J --> K["β Send to user"]
π Quick Reference
| Task | Function | Key Point |
|---|---|---|
| Send any file | send_file() |
Full path needed |
| Send from folder | send_from_directory() |
Safer, checks path |
| Clean filename | secure_filename() |
Always use on uploads |
| Build paths | os.path.join() |
Works on all systems |
| Check exists | os.path.exists() |
Prevent 404 errors |
π Remember This!
- Always use
secure_filename()on uploaded files - Use
send_from_directory()for user uploads - Set
MAX_CONTENT_LENGTHto limit file sizes - Check file extensions before saving
- Create folders with
os.makedirs(exist_ok=True)
π You did it! You now know how to safely handle files in Flask. Your digital mailroom is secure and ready for action!
