FastAPI cookbook · Intermediate
Upload files directly to S3 from a FastAPI app
Let clients upload large files without streaming them through your API servers.
Approach
Your API authorizes the upload and returns a pre-signed POST (or PUT) for S3 with a generated key, content-type and size conditions, valid for a few minutes. The client uploads directly to S3, then notifies your API, which verifies the object (size/type, optional virus scan) before using it.
Solution
import uuid
import boto3
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field
s3 = boto3.client("s3", region_name="eu-west-1")
BUCKET = "acme-uploads"
ALLOWED = {"image/png", "image/jpeg", "application/pdf"}
router = APIRouter(prefix="/uploads")
class UploadRequest(BaseModel):
filename: str = Field(max_length=200)
content_type: str
size: int = Field(gt=0, le=50 * 1024 * 1024)
@router.post("")
async def create_upload(body: UploadRequest, user: CurrentUser):
if body.content_type not in ALLOWED:
raise HTTPException(415, "Unsupported file type")
key = f"users/{user.id}/{uuid.uuid4().hex}"
post = s3.generate_presigned_post(
Bucket=BUCKET, Key=key,
Fields={"Content-Type": body.content_type},
Conditions=[{"Content-Type": body.content_type}, ["content-length-range", 1, body.size]],
ExpiresIn=300,
)
return {"key": key, "url": post["url"], "fields": post["fields"]}
@router.post("/{key:path}/complete")
async def complete_upload(key: str, user: CurrentUser, db: DB):
if not key.startswith(f"users/{user.id}/"):
raise HTTPException(403, "Not your upload")
head = s3.head_object(Bucket=BUCKET, Key=key) # verify it exists and check size/type
await files.register(db, user.id, key, head["ContentLength"], head["ContentType"])
return {"status": "ok"}
Pitfalls
- Letting clients choose the object key (overwrites, path tricks).
- Long-lived presigned URLs.
- Calling blocking boto3 in async endpoints for heavy operations (use
run_in_threadpoolor aioboto3).
Variations
- Multipart uploads for very large files.
- Trigger a Lambda/worker on
ObjectCreatedto scan and generate thumbnails.
Further reading: boto3: Presigned URLs
Reference: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-presigned-urls.html
More FastAPI recipes
- Add liveness and readiness endpoints to FastAPI
- Add OpenTelemetry tracing to FastAPI
- Add request IDs and structured logs to FastAPI
- Build a WebSocket chat endpoint in FastAPI
- Cache FastAPI responses in Redis
- Call external APIs with retries and timeouts from FastAPI
- Implement role-based access control in FastAPI
- Isolate tenants in a multi-tenant FastAPI app