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; }