Streaming large file downloads efficiently
Memory-safe file delivery.
use StreamingResponse with a generator that yields chunks (or FileResponse for an on-disk file), set media_type and a Content-Disposition header, so memory stays flat regardless of file size.
WHAT THIS TESTS Whether you understand streaming responses and can avoid loading a large payload entirely into memory, which would not scale and could crash the worker.
A GOOD ANSWER COVERS Two idiomatic options. For a file already on disk, FileResponse is simplest: you return FileResponse(path, media_type=..., filename=...) and Starlette streams it from disk and sets headers, including Content-Disposition, for you. For content you generate or read in pieces, including from another service or a database cursor, use StreamingResponse, passing an iterator or generator that yields chunks. A common pattern is a generator that opens the file and yields fixed-size blocks (for example reading 64 KB at a time) so only one chunk is in memory at once; an async generator works too. You set media_type to the correct content type and add headers={'Content-Disposition': 'attachment; filename=...'} so browsers download rather than render it. The key property is constant memory regardless of total size, because each chunk is sent before the next is read.
COMMON WRONG ANSWERS Reading the whole file with f.read() then returning it, buffering everything in RAM. Returning a bytes object directly for a multi-gigabyte file. Forgetting the Content-Disposition header so the file displays inline. Using a blocking read loop that stalls the event loop instead of chunked or threaded I/O.
LIKELY FOLLOW-UPS FileResponse versus StreamingResponse, when to pick each? How do you support range requests or resumable downloads? How do you avoid blocking the event loop while reading?
ONE CONCRETE EXAMPLE def iterfile(): with open(path, 'rb') as f: while chunk := f.read(65536): yield chunk; then return StreamingResponse(iterfile(), media_type='application/octet-stream', headers={'Content-Disposition': 'attachment; filename=report.csv'}). Memory stays around one chunk even for a 5 GB file. If the file simply sits on disk, return FileResponse(path, filename='report.csv') instead.
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.