FastAPI: From Basics to Production · Building Real APIs
File Uploads and Streaming Responses
Accept uploads safely and stream large responses.
Accept files with UploadFile (spooled to disk for large files) and form fields with Form() (requires python-multipart, included in fastapi[standard]). Validate size and content type, stream to object storage rather than reading everything into memory, and generate your own filenames. Use StreamingResponse to send large exports or server-sent events incrementally, and FileResponse for files on disk.
from fastapi import File, Form, HTTPException, UploadFile
from fastapi.responses import StreamingResponse
MAX_BYTES = 10 * 1024 * 1024
ALLOWED = {"image/png", "image/jpeg", "application/pdf"}
@app.post("/documents", status_code=201)
async def upload(title: Annotated[str, Form(max_length=200)], file: Annotated[UploadFile, File()]):
if file.content_type not in ALLOWED:
raise HTTPException(415, "Unsupported file type")
size = 0
key = f"docs/{uuid.uuid4().hex}"
async with storage.writer(key) as out:
while chunk := await file.read(1024 * 1024):
size += len(chunk)
if size > MAX_BYTES:
raise HTTPException(413, "File too large")
await out.write(chunk)
return {"key": key, "title": title, "size": size}
@app.get("/exports/orders.csv")
async def export_orders(db: DB):
async def rows():
yield "id,total\n"
async for order in orders.stream_all(db):
yield f"{order.id},{order.total_cents}\n"
return StreamingResponse(rows(), media_type="text/csv")
Common pitfalls
await file.read()on huge files, loading everything into memory.- Trusting
content_typealone — sniff file signatures for sensitive cases. - Missing size limits at the reverse proxy too (
client_max_body_size).
Key points
- UploadFile + chunked reads
- Validate type and size; own filenames
- StreamingResponse for large outputs
Further reading: FastAPI: Request files
Practice questions
Why read uploads in chunks?
Answer
Reading the entire file at once can exhaust memory for large uploads; chunks keep memory bounded and allow enforcing size limits early.
When would you use StreamingResponse?
Answer
For large or long-running outputs (CSV exports, proxying downloads, server-sent events) so data is sent incrementally without building it all in memory.
Open in the app to mark lessons complete, take notes and track your progress.