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.

showFile(blob){
  // It is necessary to create a new blob object with mime-type explicitly set
  // otherwise only Chrome works like it should
  var newBlob = new Blob([blob], {type: "application/pdf"})

  // IE doesn't allow using a blob object directly as link href
  // instead it is necessary to use msSaveOrOpenBlob
  if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(newBlob);
    return;
  } 

  // 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";
  link.click();
  setTimeout(function(){
    // For Firefox it is necessary to delay revoking the ObjectURL
    window.URL.revokeObjectURL(data);
  }, 100);
}

fetch([url to fetch], {[options setting custom http-headers]})
.then(r => r.blob())
.then(showFile)

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

This Post Has 65 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?

    1. Anders Poulsen

      I believe that is what “link.download” does.

    2. julian

      great post – was working on firefox so needed that newBlob trick! Thanks!

  2. Habr

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

    1. Anders Poulsen

      No, I’m not sure how to specify the filename in case you window.open(data) with a data URI.

      1. Nareshbabu

        Hi I had issue with download file with file extension ,I have searched in Google but i got jayway blog ,then I just made little customized ,the data/file Content coming from a URI or a Rest service .

        Details as below

  3. Venkat

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

    1. Anders Poulsen

      It should work in FireFox as it is.

  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.

    1. 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.

    1. 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.

  5. Viswa

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

    1. 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?

  6. 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}

    1. Cyril Bioley

      Indeed.
      Thanks Raul!

      1. Tom Nuorivaara

        A small correction to the setTimout parenthesis and braces:

        setTimeout(function(){
        // For Firefox it is necessary to delay revoking the ObjectURL
        document.body.removeChild(link);
        window.URL.revokeObjectURL(data);
        }, 100);

  7. Boysie

    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

  8. 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
    })
    }
    }())

    1. Jaslin

      Hi I just have a quick question, If I were to use a Base64 string to encode the URI as the file to be displayed as a PDF in Internet Explorer. How should I work around getting that?

  9. David Bell

    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!!!

  10. Amit Chaudhary

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

  11. 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();
    };
    };

  12. 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
    }

  13. 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);

  14. Suresh

    Great work done by you ,you save my three day’s work. thanks

  15. Akash

    How to print a dynamically generated pdf file in internet explorer?

  16. Pallav

    What if the file size is quite big say 1GB?

  17. Rohit

    hi Guys,

    What about if AngularJS? I’m also facing the same problem with Mozilla Firefox. Above solution is not working with the Mozilla & Safari.
    Pls. help.

    Note: I’m using AngularJS in front end and API services are written in Spring.

  18. Bharat Sewani

    Hi Anders,

    With IE11 browser, It does not open the pdf in new tab/window. Instead of that it gives the option to download. How to open pdf in new tab/window in IE11

    1. Sham

      Hi Bharat, i am facing something similar issue, did you managed to open the file in new tab/window directly rather then downloading? please let me know if you came across the solution! Thanks in advance

  19. Sebastiaan

    This is a solution which worked for us:

    let blob = new Blob([xhr.responseText], { type: contentType });

    // IE doesn’t allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(blob, fileName);
    return;
    }

    // For other browsers:
    // Create a link pointing to the ObjectURL containing the blob.
    const data = window.URL.createObjectURL(blob);
    var link = document.createElement(‘a’);
    link.href = data;
    link.download = fileName;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);

  20. jaslin

    Hi, I really need your help. I am using Vue.js also. But instead of a url to fetch I have a Base64 string to use! Is that possible ? Would you know how to ?

  21. Andrey

    It only downloads a promise for me instead of an actual PDF. Whet am i dong wrong? (creates a PDF with following: “[object Promise]”)

  22. Lucas

    Thanks!
    Is it possible to add text to the blob?

  23. Marcelo Sampaio

    Thank you very much. It’s 1pm in the saturday and this was the last thing I should do today before having fun. You are a life saver!

  24. kannathasan

    how to open pdf in new window using javascript with customized background color

  25. devesh kumar

    thanks you made my day….

  26. mounica

    Hi Anders Poulsen,
    could you please help me , I am trying to open the file in new tab of the browser for the download file functionality . My web API is sending the base64 string and when I am calling the web api , I am getting the following error: unexpected token in during parsing the Json. could you please tell me how to accept and parse the base 64 string and how to open base64 file and open it in the new tab of the web browser. (I am able to download the file and I cann’t able to open the file in new tab , it showing some error: file to load the content)
    could you please help in this.

  27. Francis

    Thanks a bunch man, it works in React

  28. Francis

    THanks you so much man…it works on React

  29. Bibi

    Thanks man. It took me 10 minutes to make my file download through Ajax work with Chrome and completely fruitless to make it work with IE. Thanks to you it finally worked.

  30. Ani

    I am using ReactJs, am able to download the file and I cann’t able to open the file in new tab , it showing error : Failed to load PDF document.
    Could you please help in this…

    1. Sam

      Hello Ani, have you found a solution to this, I am having the same problem in Vue js

  31. Jason

    I am using this but I have a question. Is there any way to receive a callback once the download is finished? My issue is that when a user clicks the download nothing happens for about 20-30 seconds then the download appears as finished (100%). I can’t figure out how to know notify the user that the download is happening with a loading screen.

  32. mujeeb

    hi i want to fetch array of video files from server using javascript or jquery guide me.

  33. HansM

    Your set timeout function seems to be wrong:

    setTimeout(function(){
    // For Firefox it is necessary to delay revoking the ObjectURL
    window.URL.revokeObjectURL(data);
    , 100}

    should be:

    setTimeout(function(){ window.URL.revokeObjectURL(data); }, 100);

    1. Anders Poulsen

      You are right.

  34. HansM

    As far as I have tested this, link.click() does not (or no longer, don’t know) work in Firefox.

    I tried the solution from Venkat under https://stackoverflow.com/questions/30694453/blob-createobjecturl-download-not-working-in-firefox-but-works-when-debugging. However, the only solution that worked for me (In Chrome, Edge and Firefox) was this one: https://stackoverflow.com/questions/32225904/programmatical-click-on-a-tag-not-working-in-firefox#

    It uses link.dispatchEvent(new MouseEvent(`click`)); instead of link.click();

  35. venkatesh

    Thanks. It’s working for chrome and IE 11.

  36. L-Bloch

    Hi Anders Poulsen

    For this to work in ie 11 and Edge, there need to be and file extension to this line:
    window.navigator.msSaveOrOpenBlob(newBlob, “file-name-here.pdf”);
    Otherwise the file has no name and will not be opened in ie11 and edge.

    1. Anders Poulsen

      Thanks, man, appreciate it!

  37. Yatin

    Hey Anders,

    Thanks for writing such a nice post.
    In my case, the server streams the file. From my understanding when creating a blob, we wait for the entire file to be read into the blob and then create a URL from the blob.
    Is there a way where i can start the download as soon as receive the response stream and not wait for the entire file?

  38. Adis

    When i click on a link in IE it opens in Edge.

  39. Katherin Carrero

    Thanks my friend, just what I needed ♥

  40. Divya

    Hi ….I am not able to download the file with its file name in secondry window. Is there any approach to solve this in angular js? Facing this issue on both Chrome and firrefox

  41. sam

    Hi, thank you for you sharing, but its not work for Edge :(
    I’m use Anguar 6
    This is my code typescript :
    // IE and Edge
    if (window.navigator && window.navigator.msSaveOrOpenBlob)
    {
    window.navigator.msSaveOrOpenBlob(blob, ‘file.docx’);
    }
    else // others
    {
    saveAs(blob, ‘file.docx’);
    . …..
    }

  42. Robert Tessler

    A really excellent article, exactly what I was looking for and incredibly useful.
    thanks

  43. Chris Keene

    Excellent article. Very difficult to find clear examples of fetching PDF files. Was able to adapt it to render in memory-only, display, and then remove the blob. Now I can display a PDF that leaves virtually no way to save/download/print the PDF file. Exactly what our requirements are.

  44. Eric

    Thank you, I couldn’t find any solution for ie11 and pdf blobs!

  45. Some Dude

    Sick solution. Thank you very very very much. Incredible. So good. I love it.

  46. FIRDOUS

    Thanks buddy,

    pdf Blob from my site was getting downloaded as jpg but ater applying the above given method it got resolved

Leave a Reply