This succinct, straight-to-the-point article will walk you through a couple of different ways to return PDF files in FastAPI. In the upcoming examples, we’ll use this sample PDF file:
https://api.slingacademy.com/v1/sample-data/files/just-text.pdf
Save it in the same folder as your Python script (and rename it to sample.pdf
).
Using FileResponse with a file path
This solution uses the FileResponse
class from FastAPI to return the PDF file from a file path on the disk. The steps to get the job done are listed below:
- Import
FileResponse
fromfastapi.responses
. - Create a
FileResponse
object with the file path as the content, the media type asapplication/pdf
, and optionally, the headers as a dictionary with theContent-Disposition
key and a value that specifies the filename and whether to view or download the file (inline
for viewing,attachment
for downloading). - Return the
FileResponse
object from the endpoint function.
Complete example:
from fastapi import FastAPI
from fastapi.responses import FileResponse
app = FastAPI()
@app.get("/pdf")
async def get_pdf():
# To view the file in the browser, use "inline" for the media_type
headers = {
"Content-Disposition": "inline; filename=sample.pdf"
}
# Create a FileResponse object with the file path, media type and headers
response = FileResponse("sample.pdf", media_type="application/pdf", headers=headers)
# Return the FileResponse object
return response
The result (at http://localhost:8000/pdf
):
If you want to download the file immediately instead of viewing it on the web browser, use these headers:
headers = {"Content-Disposition": "attachment; filename=sample.pdf"}
This approach is simple, neat, and works well for both small and large files. It reads the file asynchronously and sends it as a stream.
Using StreamingResponse with a file-like object
The main idea here is to use the StreamingResponse
class from FastAPI to return the PDF file as a stream of data from a file-like object, such as a file handle or a BytesIO
buffer.
The steps:
- Import
StreamingResponse
fromfastapi.responses
. - Open or create a file-like object that contains the PDF data, using
open
orio.BytesIO
. - Create a
StreamingResponse
object with the file-like object as the content, the media type asapplication/pdf
, and optionally, the headers as a dictionary with theContent-Disposition
key and a value that specifies the filename and whether to view or download the file. - Return the
StreamingResponse
object from the endpoint function.
Code example:
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import io
app = FastAPI()
@app.get("/pdf")
def get_pdf():
# Open the PDF file from the disk as a file-like object
f = open("sample.pdf", "rb")
# To view the file in the browser, use "inline" for the media_type
headers = {"Content-Disposition": "inline; filename=sample.pdf"}
# Create a StreamingResponse object with the file-like object, media type and headers
response = StreamingResponse(f, media_type="application/pdf", headers=headers)
# Return the StreamingResponse object
return response
This approach is less efficient and more verbose than the preceding one, but it is more flexible and customizable for sending files from other sources, such as remote URLs or databases. It also allows you to manipulate or transform the data before sending it, such as applying compression or encryption.
Using Response with file bytes and headers
In this approach, we’ll use the Response
class from FastAPI to return a PDF file as a byte stream, along with the appropriate headers and media type. The steps to implement this solution are:
- Import
Response
fromfastapi
. - Read the PDF file from the disk or memory as a byte stream, using
open
orio.BytesIO
. - Create a
Response
object with the file bytes as the content, the media type asapplication/pdf
, and the headers as a dictionary with theContent-Disposition
key and a value that specifies the filename and whether to view or download the file. - Return the
Response
object from the endpoint function.
Code example:
from fastapi import FastAPI, Response
app = FastAPI()
@app.get("/pdf")
def get_pdf():
# Read the PDF file from the disk as a byte stream
with open("sample.pdf", "rb") as f:
pdf_bytes = f.read()
# To view the file in the browser, use "inline" for the media_type
headers = {"Content-Disposition": "inline; filename=sample.pdf"}
# Create a Response object with the file bytes, media type and headers
response = Response(pdf_bytes, media_type="application/pdf", headers=headers)
# Return the Response object
return response
This approach isn’t efficient for large PDF files, since it loads the entire file into memory before returning it. Another downside is that it does not support streaming or chunking of the file data.
Note: If you want to download more sample PDF files for practice, check this article: Free Sample PDF Files for Learning & Practice.