Code: Download file from backend in an AJAX manner (Typescript)

User story:

  • The backend sends back a file for you to download, using headers such as “Content-Disposition” and “Content-Type: application/pdf”
  • Upon request, the browser will download the file for me in an AJAX manner, so I can show a progress bar
  • If backend sends an error in JSON format, I want to see the error

Solution – libraries:

  • Axios – or anything else you prefer
  • js-file-download
  • content-disposition — Used to extract file name from the header. You can write your own code to do the job without uing this library

The code:

import axios, {AxiosResponse, ResponseType as AxiosResponseType} from "axios";
import fileDownload from "js-file-download";
import contentDisposition, {ContentDisposition} from "content-disposition";


const data = {"id": [1, 2, 3]};

const responsePromise: Promise<AxiosResponse<AxiosResponseType>> = axios.request({
    method: "post",
    url: "http://some-download-backend",
    responseType: "blob",  //or anything else defined in AxiosResponseType
    data: data,
});

responsePromise.then((response: AxiosResponse<AxiosResponseType>) => {
    const filename = getAttachmentFilename(response.headers);
    const contentType = getContentType(response.headers);
    fileDownload(response.data, filename!, contentType);
}).catch((error) => {
    if (error && error.response) {
        //the error response is a blob. Need to convert it to text for parsing
        const fileReader = new FileReader();
        fileReader.onload = function (e) {
            const dataText = fileReader.result as string;
            error.response.data = JSON.parse(dataText); //because we assume the backend error is application/json
            //show the error returned by backend
        }
        fileReader.readAsText(error.response.data);
    } else {
        //show some generic error
    }
});


function getAttachmentFilename(headers?: Record<string, any>): string | undefined {
    if (!headers) {
        return undefined;
    }
    const dispositionHeader = headers["content-disposition"] as string;
    if (!dispositionHeader) {
        return undefined;
    }
    const dispositionObject: ContentDisposition = contentDisposition.parse(dispositionHeader)
    if (!dispositionObject) {
        return undefined;
    }
    return dispositionObject.parameters.filename;
}

function getContentType(headers?: Record<string, any>): string | undefined {
    if (!headers) {
        return undefined;
    }
    return headers["Content-Type"] as string;
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.