FastAPI: How to download bytes through the API

Question:

Is there a way to download a file through FastAPI? The files we want are located in an Azure Datalake and retrieving them from the lake is not an issue, the problem occurs when we try to get the bytes we get from the datalake down to a local machine.

We have tried using different modules in FastAPI such as starlette.responses.FileResponse and fastapi.Response with no luck.

In Flask this is not an issue and can be done in the following manner:

from io import BytesIO
from flask import Flask
from werkzeug import FileWrapper

flask_app = Flask(__name__)

@flask_app.route('/downloadfile/<file_name>', methods=['GET'])
def get_the_file(file_name: str):
    the_file = FileWrapper(BytesIO(download_file_from_directory(file_name)))
    if the_file:
        return Response(the_file, mimetype=file_name, direct_passthrough=True)

When running this with a valid file name the file automatically downloads. Is there equivalent way to this in FastAPI?

Solved

After some more troubleshooting I found a way to do this.

from fastapi import APIRouter, Response

router = APIRouter()

@router.get('/downloadfile/{file_name}', tags=['getSkynetDL'])
async def get_the_file(file_name: str):
    # the_file object is raw bytes
    the_file = download_file_from_directory(file_name)
    if the_file:
        return Response(the_file)

So after a lot of troubleshooting and hours of looking through documentation, this was all it took, simply returning the bytes as Response(the_file).

Asked By: Markus

||

Answers:

As far as I know, you need to set media_type to the adequate type. I did that with some code a year ago and it worked fine.

@app.get("/img/{name}")
def read(name: str, access_token_cookie: str=Cookie(None)):
  r = internal.get_data(name)
  if r is None:
    return RedirectResponse(url="/static/default.png")
  else:
    return Response(content=r["data"], media_type=r["mime"])

r is a dictionary with the data as raw bytes and mime the type of the data as given by PythonMagick.

Answered By: Matthieu Brucher

After some more troubleshooting I found a way to do this.

from fastapi import APIRouter, Response

router = APIRouter()

@router.get('/downloadfile/{file_name}', tags=['getSkynetDL'])
async def get_the_file(file_name: str):
    # the_file object is raw bytes
    the_file = download_file_from_directory(file_name)
    if the_file:
        return Response(the_file)

So after a lot of troubleshooting and hours of looking through documentation, this was all it took, simply returning the bytes as Response(the_file) with no extra parameters and no extra formatting for the raw bytes object.

Answered By: Markus

To add a custom filename to @Markus’s answer, in case your api’s path doesn’t end with a neat filename or you want to determine a custom filename from server side and give to the user:

from fastapi import APIRouter, Response

router = APIRouter()

@router.get('/downloadfile/{file_name}', tags=['getSkynetDL'])
async def get_the_file(file_name: str):
    # the_file object is raw bytes
    the_file = download_file_from_directory(file_name)
    filename1 = make_filename(file_name) # a custom filename
    headers1 = {'Content-Disposition': f'attachment; filename="{filename1}"'}
    if the_file:
        return Response(the_file, headers=headers1)
Answered By: Nikhil VJ
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.