How to return a PDF file from in-memory buffer using FastAPI?
Question:
I want to get a PDF file from s3 and then return it to the frontend from FastAPI backend.
This is my code:
@router.post("/pdf_document")
def get_pdf(document : PDFRequest) :
s3 = boto3.client('s3')
file=document.name
f=io.BytesIO()
s3.download_fileobj('adm2yearsdatapdf', file,f)
return StreamingResponse(f, media_type="application/pdf")
This API returns 200
status code, but it does not return the PDF file as a response.
Answers:
As the entire file data are already loaded into memory, there is no actual reason for using StreamingResponse
. You should instead use Response
, by passing the file bytes (use BytesIO.getvalue()
to get the bytes containing the entire contents of the buffer), defining the media_type
, as well as setting the Content-Disposition
header, so that the PDF file can be either viewed in the browser or downloaded to the user’s device. For more details have a look at this answer, as well as this and this answer. Additionally, as the buffer
is discarded when the close()
method is called, you could also use FastAPI/Starlette’s BackgroundTasks
to close the buffer
after returning the response, in order to release the memory. Alternatively, you could get the bytes using pdf_bytes = buffer.getvalue()
, then close the buffer using buffer.close()
and finally, return Response(pdf_bytes, headers=...
. Example:
from fastapi import Response, BackgroundTasks
@app.get("/pdf")
def get_pdf(background_tasks: BackgroundTasks):
buffer = io.BytesIO()
# ...
background_tasks.add_task(buffer.close)
headers = {'Content-Disposition': 'inline; filename="out.pdf"'}
return Response(buffer.getvalue(), headers=headers, media_type='application/pdf')
To have the PDF file downloaded rather than viewed in the borwser, use:
headers = {'Content-Disposition': 'attachment; filename="out.pdf"'}
I want to get a PDF file from s3 and then return it to the frontend from FastAPI backend.
This is my code:
@router.post("/pdf_document")
def get_pdf(document : PDFRequest) :
s3 = boto3.client('s3')
file=document.name
f=io.BytesIO()
s3.download_fileobj('adm2yearsdatapdf', file,f)
return StreamingResponse(f, media_type="application/pdf")
This API returns 200
status code, but it does not return the PDF file as a response.
As the entire file data are already loaded into memory, there is no actual reason for using StreamingResponse
. You should instead use Response
, by passing the file bytes (use BytesIO.getvalue()
to get the bytes containing the entire contents of the buffer), defining the media_type
, as well as setting the Content-Disposition
header, so that the PDF file can be either viewed in the browser or downloaded to the user’s device. For more details have a look at this answer, as well as this and this answer. Additionally, as the buffer
is discarded when the close()
method is called, you could also use FastAPI/Starlette’s BackgroundTasks
to close the buffer
after returning the response, in order to release the memory. Alternatively, you could get the bytes using pdf_bytes = buffer.getvalue()
, then close the buffer using buffer.close()
and finally, return Response(pdf_bytes, headers=...
. Example:
from fastapi import Response, BackgroundTasks
@app.get("/pdf")
def get_pdf(background_tasks: BackgroundTasks):
buffer = io.BytesIO()
# ...
background_tasks.add_task(buffer.close)
headers = {'Content-Disposition': 'inline; filename="out.pdf"'}
return Response(buffer.getvalue(), headers=headers, media_type='application/pdf')
To have the PDF file downloaded rather than viewed in the borwser, use:
headers = {'Content-Disposition': 'attachment; filename="out.pdf"'}