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