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)

2
  • can you share the exact API URL? Commented Dec 3, 2019 at 15:47
  • Something like https://www.jianjunchen.com/papers/CORS-USESEC18.slides.pdf Does it matter? Commented Dec 3, 2019 at 16:36

3 Answers 3

11

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");
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the answer, the {responseType: 'blob'} was the key :)
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.

3 Comments

Thanks for the answer, but what is the type of x cause x => x.blob() throws x.blob is not a function
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.
Thanks, it was a good point to know, But as I use Axios, had to accept the other answer. Thanks again for your input
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);
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.