How to open a pdf downloaded from an API with JavaScript

On my most recent assignment I was faced with the challenge of downloading a pdf file from an API and displaying it in the browser.

Normally, the API would provide a json-object with a link to the pdf, which can then be used to open a new browser tab/window and the browser or platform takes care of the rest.

In this case however the API was designed for native mobile apps and required the presence of a custom ‘api-key’ http-header, which made it impossible to just provide the url to the browser.

To solve this it was necessary to download the file to memory with an XHR request and then get the browser to open or download it with whatever plugin/UI it normally uses for pdf file.

For the XHR request we use the Fetch API with the whatwg-fetch polyfill. In essence the Fetch API fetch() method returns a response, from which a blob can be created. This blob object can be use to create an objectURL, which can then be used as href in a link.

 

This solution works on Chrome, Safari, Firefox, Opera, IE11 and Edge.

21 Comments

  1. Habr

    Thank you soooo much! You saved my day.
    In case of IE I use window.navigator.msSaveOrOpenBlob(newBlob, filename) however would you know how to set the title of a new window or file name in “other” browsers?

  2. Habr

    …I meant in case I use window.open(data) instead of link

  3. Venkat

    Thanks for this, is there a way to make this work in FireFox?

  4. Venkata Reddy

    For me in Firefox the file is not getting downloaded. But working perfectly in all other browsers. Also you mentioned “This solution works on Chrome, Safari, Opera, IE11 and Edge” in this article.

    • Anders Poulsen

      You are right. I don’t know how I did not think to also test Firefox. I don’t know how to fix it right at this moment, however.

  5. Venkat

    I had to set timeout for firefox to fix it, ref: https://stackoverflow.com/questions/30694453/blob-createobjecturl-download-not-working-in-firefox-but-works-when-debugging. Probably you can update this article. Thanks again for this article :).

    • Anders Poulsen

      Thanks for the solution. At first I did have a timeout before I removed the resource, but it turned out not to be necessary in the browsers I remembered to test with. I’ll have to update the article. Thanks again.

  6. Viswa

    This is not working in safari, it is opening as unknown file and saving as data file. Is there any solution for that

    • Anders Poulsen

      Are you using Safari on OS X or iOS? And what version?
      Could it be that the server is not setting the correct mime-type on the http response?

  7. Raul

    Thank you. This work in FireFox with the below modification

    // For other browsers:
    // Create a link pointing to the ObjectURL containing the blob.
    const data = window.URL.createObjectURL(newBlob);
    var link = document.createElement(‘a’);
    link.href = data;
    link.download=”file.pdf”;
    document.body.appendChild(link);
    link.click();
    setTimeout(function(){
    // For Firefox it is necessary to delay revoking the ObjectURL
    document.body.removeChild(link);
    window.URL.revokeObjectURL(data);
    , 100}

  8. Hello

    I am not a programmer –

    Is it possible to have a complete script

    for example

    Where on the web page do you put the various elements
    head>

    </head?

    </html?

    Thanks

  9. Brian DeMasse

    Thank you so much for this! I had a really difficult time just showing a PDF in Vue.js. I altered it a little, below:

    const showPDF = (function () {
    return function (blob, fileName) {
    var newBlob = new Blob([blob], {type: ‘application/pdf’})
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(newBlob)
    return
    }
    const data = window.URL.createObjectURL(newBlob)
    var link = document.createElement(‘a’)
    link.href = data
    link.click()
    window.open(data)
    setTimeout(function () {
    document.body.removeChild(link)
    window.URL.revokeObjectURL(data)
    100
    })
    }
    }())

  10. Ok, I see I’m far from the only one who is so so happy this exists but you totally saved my day and my code works perfectly now. I used this with a react application calling a API in node/express which generated PDF files with pdfkit. React routing really got in the way of my prior approach but this worked out of the box so thank you thank you thank you!!!

  11. Amit Chaudhary

    This is not working for me in Safari. Applied the same code but no luck. Please suggest me any modifications required.

  12. Shubham Saurabh

    I need to change the name of the file dynamically how can I implement this ? OR How can I pass an argument Inside a blob action ? In my second then where I am passing the patient_id as an argument, it gives me an undefined while I have a patient Id working. Please solve this issue.
    export const uploadedDocumentDownload = (docId, patient_id) => {
    console.log(‘action patient Id———-‘, patient_id);
    return (dispatch, getState) => {
    dispatch(actionCreator(patientActionTypes.uploaded_document_download.REQUEST));
    fetch(${UPLOADED_DOCUMENT_DOWNLOAD}/${docId}, {
    method: ‘GET’,
    headers: jsonApiHeader(getState().login.access_token, ‘application/json’),
    })
    .then((r) => r.blob())
    .then((patient_id) => downloaduploadedDocumentsOnAPICall(patient_id))
    .then();
    };
    };

  13. jaipal

    Hi , is there any solution for Chrome browser in IPAD . I could not open the pdf in chrome in IPAD.
    Problem : in Safari/IPAD i can able to open the blob and PDF . But in Chrome/IPAD i can able to open blob but could not load the PDF file .
    Is there any solution for this.

    I tried in this way , but could not work.

    function openBlob (name, blob, windowOpenReference) {
    // simulateAnchorClick(name, blob, false)
    // for ie use window.navigator
    const userAgent = window.navigator.userAgent
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(blob)
    } else if (userAgent.match(‘CrIOS’)) { // Chrome IOS
    const reader = new window.FileReader()
    reader.onloadend = function () { window.open(reader.result) }
    reader.readAsDataURL(blob)
    } else {
    // every other browser users window.open
    const url = window.URL.createObjectURL(blob)
    windowOpenReference.location.href = url
    }

  14. Manichandra

    Thanks. Its working as expected in Chrome but not IE. So i am using file-saver to make it work for all the browsers.
    However when i try to open a pdf in a new tab it’s not working. I am using the below code . Any suggestions to make it work ? I am Angular btw
    const blob = new Blob([res.blob()], { type: res.blob().type });
    const fileURL = URL.createObjectURL(blob);
    window.open(fileURL);

Leave a Reply