pythonexcelfastapimedia-type

How to return and download Excel file using FastAPI?


How do I return an excel file (version: Office365) using FastAPI? The documentation seems pretty straightforward. But, I don't know what media_type to use. Here's my code:

import os
from fastapi import FastAPI
from fastapi.responses import FileResponse
from pydantic import BaseModel
from typing import Optional

excel_file_path = r"C:\Users\some_path\the_excel_file.xlsx"

app = FastAPI()

class ExcelRequestInfo(BaseModel):
    client_id: str


@app.post("/post_for_excel_file/")
async def serve_excel(item: ExcelRequestInfo):
    # (Generate excel using item.)
    # For now, return a fixed excel.
    return FileResponse(
        path=excel_file_path,

        # Swagger UI says 'cannot render, look at console', but console shows nothing.
        media_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'

        # Swagger renders funny chars with this argument:
        # 'application/vnd.ms-excel'
    )

Assuming I get it right, how to download the file? Can I use Swagger UI generated by FastAPI to view the sheet? Or, curl? Ideally, I'd like to be able to download and view the file in Excel.

Solution

Here's my final (edited) solution to save you from clicking about. In the course of development, I had to switch from a FileResponse to Response that returns io.BytesIO.

import io
import os.path
from fastapi.responses import Response


@router.get("/customer/{customer}/sheet")
async def generate_excel(customer: str):
    excel_file_path: str = None
    buffer: io.BytesIO = None

    # Generate the sheet.
    excel_file_path, buffer = make_excel(customer=customer)

    # Return excel back to client.
    headers = {
        # By adding this, browsers can download this file.
        'Content-Disposition': f'attachment; filename={os.path.basename(excel_file_path)}',
        # Needed by our client readers, for CORS (cross origin resource sharing).
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Headers": "*",
        "Access-Control_Allow-Methods": "POST, GET, OPTIONS",
    }
    media_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'

    return Response(
        content=buffer.getvalue(),
        headers=headers,
        media_type=media_type
    )

Solution

  • You could set the Content-Disposition header using the attachment parameter, indicating to the web browser that the file should be downloaded, as described in the answers here and here. Swagger UI will provide a Download file link for you to download the file, as soon as you execute the request.

    headers = {'Content-Disposition': 'attachment; filename="Book.xlsx"'}
    return FileResponse(excel_file_path, headers=headers)
    

    To have the file viewed in the web browser, one can use the inline, instead of attachment, parameter in the Content-Disposition header, as explained in the linked answers earlier. However, for the browser to be able to display the Excel file, one should set the correct media_type in the FileResponse (for Excel files see here), as well as .xlsx (or .xls) must be a known file extension to the browser (this is usually achieved through web browser extensions/plug-ins).