FastAPI Tutorial Part 14: File Uploads and Storage
Handle file uploads in FastAPI. Learn form data, file validation, cloud storage integration with S3, and serving static files.
Moshiour Rahman
Advertisement
Basic File Upload
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
contents = await file.read()
return {
"filename": file.filename,
"content_type": file.content_type,
"size": len(contents)
}
File Validation
from fastapi import HTTPException
ALLOWED_TYPES = ["image/jpeg", "image/png", "image/gif"]
MAX_SIZE = 5 * 1024 * 1024 # 5MB
@app.post("/upload/image")
async def upload_image(file: UploadFile = File(...)):
if file.content_type not in ALLOWED_TYPES:
raise HTTPException(400, "Invalid file type")
contents = await file.read()
if len(contents) > MAX_SIZE:
raise HTTPException(400, "File too large")
# Save file
file_path = f"uploads/{file.filename}"
with open(file_path, "wb") as f:
f.write(contents)
return {"path": file_path}
Multiple Files
from typing import List
@app.post("/upload/multiple")
async def upload_multiple(files: List[UploadFile] = File(...)):
results = []
for file in files:
contents = await file.read()
results.append({
"filename": file.filename,
"size": len(contents)
})
return {"files": results}
Form Data with Files
from fastapi import Form
@app.post("/upload/profile")
async def upload_profile(
name: str = Form(...),
email: str = Form(...),
avatar: UploadFile = File(...)
):
# Save avatar
path = f"avatars/{avatar.filename}"
with open(path, "wb") as f:
content = await avatar.read()
f.write(content)
return {
"name": name,
"email": email,
"avatar_path": path
}
AWS S3 Integration
import boto3
from botocore.exceptions import ClientError
import uuid
s3_client = boto3.client(
"s3",
aws_access_key_id="YOUR_KEY",
aws_secret_access_key="YOUR_SECRET",
region_name="us-east-1"
)
BUCKET_NAME = "my-bucket"
@app.post("/upload/s3")
async def upload_to_s3(file: UploadFile = File(...)):
file_key = f"uploads/{uuid.uuid4()}-{file.filename}"
try:
s3_client.upload_fileobj(
file.file,
BUCKET_NAME,
file_key,
ExtraArgs={"ContentType": file.content_type}
)
url = f"https://{BUCKET_NAME}.s3.amazonaws.com/{file_key}"
return {"url": url, "key": file_key}
except ClientError as e:
raise HTTPException(500, f"Upload failed: {str(e)}")
Serving Static Files
from fastapi.staticfiles import StaticFiles
app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount("/uploads", StaticFiles(directory="uploads"), name="uploads")
Complete Example
from fastapi import FastAPI, File, UploadFile, HTTPException
from typing import List
import os
import uuid
import aiofiles
app = FastAPI()
UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)
ALLOWED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".gif", ".pdf"}
MAX_FILE_SIZE = 10 * 1024 * 1024
def validate_file(file: UploadFile):
ext = os.path.splitext(file.filename)[1].lower()
if ext not in ALLOWED_EXTENSIONS:
raise HTTPException(400, f"File type {ext} not allowed")
async def save_file(file: UploadFile) -> str:
validate_file(file)
ext = os.path.splitext(file.filename)[1]
unique_name = f"{uuid.uuid4()}{ext}"
file_path = os.path.join(UPLOAD_DIR, unique_name)
async with aiofiles.open(file_path, "wb") as f:
content = await file.read()
if len(content) > MAX_FILE_SIZE:
raise HTTPException(400, "File too large")
await f.write(content)
return unique_name
@app.post("/files/upload")
async def upload_file(file: UploadFile = File(...)):
filename = await save_file(file)
return {"filename": filename, "url": f"/uploads/{filename}"}
@app.post("/files/upload-multiple")
async def upload_multiple(files: List[UploadFile] = File(...)):
results = []
for file in files:
filename = await save_file(file)
results.append({"filename": filename})
return {"files": results}
Summary
| Feature | Method |
|---|---|
| Single file | UploadFile = File(...) |
| Multiple files | List[UploadFile] = File(...) |
| With form data | Combine Form() and File() |
| Cloud storage | boto3 for S3 |
Next Steps
In Part 15, we’ll explore Testing FastAPI Applications - writing comprehensive tests for your API.
Series Navigation:
- Part 1-13: Previous parts
- Part 14: File Uploads (You are here)
- Part 15: Testing
Advertisement
Moshiour Rahman
Software Architect & AI Engineer
Enterprise software architect with deep expertise in financial systems, distributed architecture, and AI-powered applications. Building large-scale systems at Fortune 500 companies. Specializing in LLM orchestration, multi-agent systems, and cloud-native solutions. I share battle-tested patterns from real enterprise projects.
Related Articles
FastAPI Tutorial Part 11: Background Tasks and Celery
Handle long-running operations in FastAPI. Learn built-in BackgroundTasks, Celery integration, task queues, and async processing patterns.
PythonFastAPI Tutorial Part 7: CRUD Operations - Build a Complete REST API
Build production-ready CRUD APIs with FastAPI. Learn RESTful patterns, pagination, filtering, bulk operations, and best practices for real-world applications.
PythonFastAPI Tutorial Part 5: Dependency Injection - Share Logic Across Endpoints
Master FastAPI dependency injection for clean, reusable code. Learn database sessions, authentication, pagination, and complex dependency chains.
Comments
Comments are powered by GitHub Discussions.
Configure Giscus at giscus.app to enable comments.