Python FastAPI Tutorial (Part 12): File Uploads - Image Processing, Validation, and Storage
TL;DR
Corey Schafer demonstrates implementing secure profile picture uploads in FastAPI using Pillow for image resizing and format standardization, with proper handling of CPU-bound tasks in async contexts and safe file transaction patterns to prevent data loss.
🖼️ Image Processing Setup 3 insights
Process images with Pillow
Resize uploads to 300x300 pixels using LANCZOS resampling, convert to RGB format to handle transparency, fix EXIF orientation issues, and save as optimized JPEG with quality set to 85.
Handle CPU-bound work in async contexts
Offload image processing to separate threads using Starlette's run_in_threadpool to prevent blocking the async event loop during computationally intensive Pillow operations.
Generate secure unique filenames
Create UUID4 filenames instead of using client-provided names to prevent file collisions, directory traversal attacks, and security issues from malicious file names.
🔒 Security & Validation 3 insights
Validate actual file content
Verify uploaded files are valid images by attempting to open them with Pillow and catching UnidentifiedImageError rather than trusting client-provided Content-Type headers.
Enforce upload size limits
Configure a maximum file size setting (default 5MB) and validate content length after reading to protect the server from excessive memory consumption and denial-of-service attacks.
Secure API schemas
Remove image_file fields from Pydantic update schemas to prevent users from manipulating file paths directly through JSON endpoints, restricting changes to dedicated upload endpoints only.
⚡ Safe File Operations 3 insights
Maintain safe transaction ordering
Save new image files to disk before updating the database, and only delete old files after successful database commits to prevent losing user profile pictures if transactions fail.
Use dedicated upload endpoints
Create separate PATCH endpoints using UploadFile for multipart/form-data rather than mixing binary file data with JSON payloads, keeping concerns clean and manageable.
Implement proper cleanup
Delete physical profile picture files when users upload replacements or delete their accounts, but only after confirming database updates succeed to avoid orphaned files or broken references.
Bottom Line
Validate file integrity with Pillow instead of trusting client headers, process images in thread pools to avoid blocking async endpoints, and always delete old files after database commits succeed to prevent permanent data loss.
More from Corey Schafer
View all
Python FastAPI Tutorial (Part 13): Pagination - Loading More Data with Query Parameters
This tutorial demonstrates how to implement offset-based pagination in FastAPI using skip and limit query parameters, covering backend schema design with SQLAlchemy queries and frontend integration with a 'load more' button pattern.
Python FastAPI Tutorial (Part 11): Authorization - Protecting Routes and Verifying Current User
This tutorial demonstrates how to implement proper authorization in FastAPI by creating a reusable dependency that validates JWT tokens and retrieves the current user, enabling secure route protection and ownership verification while eliminating hard-coded user IDs.
Python FastAPI Tutorial (Part 10): Authentication - Registration and Login with JWT
This tutorial establishes backend authentication infrastructure for FastAPI by implementing Argon2 password hashing, JWT token management, and Pydantic Settings configuration, while updating database models and schemas to support secure user registration and login workflows.
Python FastAPI Tutorial (Part 9): Frontend Forms - Connecting JavaScript to Your API
This tutorial demonstrates how to connect JavaScript frontend forms to a FastAPI backend using the fetch API, implementing full CRUD functionality through Bootstrap modals while temporarily hardcoding user authentication and centralizing business logic like data sorting in SQLAlchemy queries.