Sending Files¶
The send_file utility in Lilya makes it easy to return files from your endpoints,
similar to Flask's send_file.
It can return files from the filesystem or from in-memory file-like objects, and supports options such as forcing downloads, setting custom filenames, and controlling cache headers.
When to Use send_file¶
Use send_file when your API or application needs to:
- Serve static files on demand (e.g., reports, exports, PDFs).
- Allow users to download dynamically generated files.
- Stream file-like objects such as
BytesIO.
For static assets (images, CSS, JS) you should use proper static file serving,
but for API endpoints that return a file, send_file is the right tool.
Basic Example¶
Returning a text file directly:
from lilya.apps import Lilya
from lilya.routing import Path
from lilya.contrib.responses.files import send_file
async def get_report(request):
return send_file("reports/summary.txt")
app = Lilya(routes=[
Path("/report", get_report)
])
Request:
curl http://localhost:8000/report
Response body will contain the content of summary.txt.
Force Download (Attachment)¶
To force the browser to download the file instead of displaying it inline:
async def download_invoice(request):
return send_file("invoices/invoice.pdf", as_attachment=True)
This adds a Content-Disposition: attachment header, prompting the browser to download the file.
Custom Filename¶
You can customize the filename shown to the user:
async def export_csv(request):
return send_file(
"exports/data.csv",
as_attachment=True,
attachment_filename="report.csv"
)
The downloaded file will appear as report.csv instead of data.csv.
Sending a File-Like Object¶
You can also send an in-memory file, such as a BytesIO:
import io
async def stream_file(request):
file_like = io.BytesIO(b"some dynamic content")
return send_file(file_like, mimetype="text/plain")
This is useful for dynamically generated content that you don't want to store on disk.
Controlling Cache¶
Set Cache-Control headers with max_age:
async def cached_pdf(request):
return send_file("docs/manual.pdf", max_age=3600)
Response will include:
Cache-Control: public, max-age=3600
API Reference¶
send_file(
filename_or_fp: Union[str, Path, IO[bytes]],
mimetype: Optional[str] = None,
as_attachment: bool = False,
attachment_filename: Optional[str] = None,
max_age: Optional[int] = None,
)
Parameters¶
filename_or_fp– Path to the file on disk (strorPath) or a file-like object (IO[bytes]).mimetype– Optional MIME type (e.g.,"application/pdf"). If not provided, Lilya attempts to infer.as_attachment– IfTrue, addsContent-Disposition: attachmentto force download.attachment_filename– Override the filename presented to the user.max_age– SetsCache-Control: public, max-age=<seconds>for caching.
With send_file, Lilya makes file downloads and streaming as simple and flexible as Flask, but fully async-native.