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 (str
orPath
) 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: attachment
to 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.