python 47 lines · 8 steps

Streaming a generated PDF from a Flask route

A Flask endpoint renders an invoice to a PDF in memory and sends it as a downloadable file.

Explained by highlit
1from io import BytesIO
2from flask import Blueprint, abort, send_file
3from reportlab.lib.pagesizes import LETTER
4from reportlab.lib.units import inch
5from reportlab.pdfgen import canvas
6 
7from .models import Invoice
8 
9bp = Blueprint("invoices", __name__)
10 
11 
12@bp.route("/invoices/<int:invoice_id>/download")
13def download_invoice(invoice_id):
14 invoice = Invoice.query.get_or_404(invoice_id)
15 if not invoice.is_finalized:
16 abort(409, description="Invoice is still a draft")
17 
18 buffer = BytesIO()
19 pdf = canvas.Canvas(buffer, pagesize=LETTER)
20 width, height = LETTER
21 
22 pdf.setFont("Helvetica-Bold", 20)
23 pdf.drawString(inch, height - inch, f"Invoice #{invoice.number}")
24 
25 pdf.setFont("Helvetica", 11)
26 pdf.drawString(inch, height - 1.4 * inch, f"Billed to: {invoice.customer.name}")
27 pdf.drawString(inch, height - 1.65 * inch, f"Date: {invoice.issued_at:%B %d, %Y}")
28 
29 y = height - 2.3 * inch
30 for item in invoice.line_items:
31 pdf.drawString(inch, y, item.description)
32 pdf.drawRightString(width - inch, y, f"${item.amount:,.2f}")
33 y -= 0.3 * inch
34 
35 pdf.setFont("Helvetica-Bold", 12)
36 pdf.drawRightString(width - inch, y - 0.2 * inch, f"Total: ${invoice.total:,.2f}")
37 
38 pdf.showPage()
39 pdf.save()
40 buffer.seek(0)
41 
42 return send_file(
43 buffer,
44 mimetype="application/pdf",
45 as_attachment=True,
46 download_name=f"invoice-{invoice.number}.pdf",
47 )
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Building files in a BytesIO buffer avoids touching disk and keeps the request self-contained.
  2. 2Validate resource state before doing expensive rendering work, returning a precise HTTP status.
  3. 3send_file with as_attachment and a download_name turns a byte stream into a browser download.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Streaming a generated PDF from a Flask route — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code