4

An Axios request to server response the content of a PDF as a binary string.

export const fetchPDFfile = async (id: string): Promise<string> => {
  const { data } = await http.get<string>(`${baseUrl}/${id}.pdf`);
  return data;
};

The response in Chrome devtools and also console logging the data is like:

%PDF-1.4
%âãÏÓ
2 0 obj <</ColorSpa ......
..........
startxref
10991
%%EOF
  • is defining string as the expected type of Axios response body, correct? or it should be (cast to) Blob?

Now I want to download this as a PDF file in the client-side. There are plenty of questions regarding this but none worked for me and also none had a clear answer.

So what I did so far was (in a React component):

    const data = await fetchPDFfile(props.id);
    const blob = new Blob([data], { type: 'application/pdf' });
    const href = window.URL.createObjectURL(blob);
    const theLink = document.createElement('a');
    theLink.href = href;
    theLink.download = props.id + '.pdf';
    document.body.appendChild(theLink);
    theLink.click();
    document.body.removeChild(theLink);

This downloads a PDF file with 3 blank pages. The number of pages is correct the original doc should bee 3 pages. But I see the white paper.

const href = window.URL.createObjectURL(data); // istead of blob throw Error.

How should I convert and download this PDF file? In general, is the process above needed, or should I directly download it from the server? (something like what cordova-plugin-file-transfer does)

Amir-Mousavi
  • 4,273
  • 12
  • 70
  • 123

3 Answers3

9

Scenario

You want the file to be downloaded when the user clicks the link.

Solution 1-

Directly put the link in <a> tag.

Cons- Error message can not be shown on the screen if something went wrong.

So it leads to the next solution.

Solution 2-

Hit the URL as an API and download the file if you get the success message. For this, I use File-server.js

**Don't forget to set the {responseType: 'blob'}, while making the request

http.get<string>(`${baseUrl}/${id}.pdf`, {responseType: 'blob'})

as we don't want the response with Content-Type: application/json

sample code:

import FileSaver from 'file-saver';

downloadPdf() {
    var blob = new Blob([data], {type: "application/pdf"});
    FileSaver.saveAs(blob, "filename");
}
2

Firstly use Blob as generic argument for Promise.

I will use fetch API as it can be tested quite easily.

fetch('https://www.jianjunchen.com/papers/CORS-USESEC18.slides.pdf').then(x => x.blob()).then(b => console.log(b.type))

This will log "application/pdf" it the file is trully pdf.

If you got a blob that is not PDF and you will re-wrap it to Blob with pdf type you might break the data. If you got trully a string and you convert it to Blob with pdf type the file will be broken as the PDF would be invalid.

If you want to know if b is trully a blob just console.log(b instanceof Blob) and it should say true. If you have recieved trully a blob you do not have to create new one as you did in new Blob([data]).

This example works just fine:

fetch('https://www.jianjunchen.com/papers/CORS-USESEC18.slides.pdf').then(x => x.blob()).then(b => {
  const url = window.URL.createObjectURL(b);
  var a = document.createElement("a");
  document.body.appendChild(a);
  a.style = "display: none";
  a.href = url;
  a.download = "a.pdf";
  a.click();
  window.URL.revokeObjectURL(url);
})

Sorry for broken code style but I was unable to paste it properly.

  • Thanks for the answer, but what is the type of `x` cause `x => x.blob()` throws x.blob is not a function – Amir-Mousavi Dec 03 '19 at 16:51
  • It is Fetch API specific. Compared to axios fethc api doesnt resolve response automaticaly so you have to call either `blob()` or `text()` or `json()` etc... Fetch returns a `Promise` with `Reponse` object that can be anything and you have to shape it. These "shaping" functions return `Promise` that contains shaped response content. Axios shapes it for you so you do not have to do that manually. Basically common code with axios is the part in the last `then` function. – Luk Deathrage Prochzka Dec 03 '19 at 21:18
  • Thanks, it was a good point to know, But as I use Axios, had to accept the other answer. Thanks again for your input – Amir-Mousavi Dec 04 '19 at 14:14
0

For nestjs

In service

async download() {
        try {
            const url = `https://example.com`;
            
            const headers = {
              Authorization: `Bearer ${this.accessToken}`,
              'Content-Type': 'application/pdf'
            };
            const response: AxiosResponse<any> = await this.httpService.get(url, { headers, responseType: 'arraybuffer' }).toPromise();
      
            return response.data;
        } catch (error) {
            throw new Error('Unable to download '+ error);
        }
    }

In Controller

@Get('download')
    async download(@Res() res) {
        const data = await this.appService.download();

        res.setHeader('Content-disposition', 'attachment; filename="file.pdf"');
        res.setHeader('Content-type', 'application/pdf');

        res.send(data);
    }
Kabilan
  • 83
  • 1
  • 8