94

I want to download the file which is coming in the form of bytes from the AJAX response.

I tried to do it this way with the help of Blob:

var blob=new Blob([resultByte], {type: "application/pdf"});
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="myFileName.pdf";
link.click();

It is in fact downloading the pdf file but the file itself is corrupted.

How can I accomplish this?

Ismoil Shokirov
  • 2,213
  • 2
  • 16
  • 32
Jahongir Rahmonov
  • 13,083
  • 10
  • 47
  • 91
  • 3
    This is a bit of a +1 comment. I'm trying to do the same thing, and with the same results - a corrupted PDF. The PDF will open, has the same number of pages I expect, but there is no text. I suspect the issue lies in either the encoding of the PDF, or the conversion of it from `[resultByes]` to a blob... I would like to hear if you came up with a solution. – runlevelsix May 19 '16 at 21:40
  • 1
    @runlevelsix, yes I figured that out! Please see my answer below and see if it works for you too – Jahongir Rahmonov May 20 '16 at 07:39

5 Answers5

204

I asked the question long time ago, so I might be wrong in some details.

It turns out that Blob needs array buffers. That's why base64 bytes need to be converted to array buffers first.

Here is the function to do that:

function base64ToArrayBuffer(base64) {
    var binaryString = window.atob(base64);
    var binaryLen = binaryString.length;
    var bytes = new Uint8Array(binaryLen);
    for (var i = 0; i < binaryLen; i++) {
       var ascii = binaryString.charCodeAt(i);
       bytes[i] = ascii;
    }
    return bytes;
 }

Here is my function to save a pdf file:

function saveByteArray(reportName, byte) {
    var blob = new Blob([byte], {type: "application/pdf"});
    var link = document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    var fileName = reportName;
    link.download = fileName;
    link.click();
};

Here is how to use these two functions together:

var sampleArr = base64ToArrayBuffer(data);
saveByteArray("Sample Report", sampleArr);
gog
  • 1,220
  • 11
  • 30
Jahongir Rahmonov
  • 13,083
  • 10
  • 47
  • 91
  • 3
    For me, the code is not working in `Firefox 58.0.2`, It executes with no errors, but the download dialog doesn't appear. I need to append the link to `body` and it works. https://bugzilla.mozilla.org/show_bug.cgi?id=1091035 – mihkov Feb 20 '18 at 21:56
  • 2
    I am returning base64 from server side and while use your method it gives me an error : Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded. – Ankita Aug 15 '18 at 07:35
  • 1
    To avoid the "failed to execute 'atob' on 'window'" error I had to return the data from my MVC action as JSON (as opposed to a FileResult / FileContentResult) – combatc2 Aug 22 '18 at 12:51
  • I had the same issue and in my case the solution was to assure that I transfer data from the server side to client side in a proper `Base64` format. Once I did it, your solution worked for me. Thanks a lot. – Mike Aug 23 '18 at 14:48
  • 1
    For those with problems with downloads not working in Firefox or IE. Change: link.click() to link.dispatchEvent(new MouseEvent(`click`, {bubbles: true, cancelable: true, view: window})); – bender Nov 23 '18 at 18:15
  • @bender, you left the 'click' without quotes: link.dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true, view: window})); – Roy Oliver Dec 20 '18 at 21:30
  • I had to add this line document.body.appendChild(link); after link.href = window.URL.createObjectURL(blob);. Worked perfectly after that! – Dan Leksell Sep 12 '19 at 16:38
  • getting error of "TypeError: base64ToArrayBuffer is not a function" when i called this method to convert bytecode to buffer – s.j Jun 08 '21 at 09:47
  • excellent! I was stuck on converting the Binary String to Byte Array. Thanks for that function! – Gutsygibbon Jul 14 '21 at 16:36
  • You saved many hours today, sir. I'll add that if you still have the initial data in scope when you initialise the blob, you can use blob.type and it will pull back other file types correctly as well (So new Blob([byte], { type: data.type })). I was setting the download link to their actual filenames, but regardless I was able to pull back PDFs, DOCs, PNGs and JPEGs like this. – boyde712 Nov 07 '22 at 06:57
32

You just need to add one extra line and it should work. Your response is byte array from your server application

var bytes = new Uint8Array(resultByte); // pass your byte response to this constructor

var blob=new Blob([bytes], {type: "application/pdf"});// change resultByte to bytes

var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="myFileName.pdf";
link.click();
Bucket
  • 7,415
  • 9
  • 35
  • 45
KrishnaSingh
  • 696
  • 1
  • 7
  • 12
  • Byte array was coming as response and I was getting an error like invalid format or extension, adding the first line above solved the problem. – MrAlbino Jul 11 '23 at 14:34
  • This works flawlessly in my browser (Firefox) and allows me to download PDF files created by pdf-lib.js. Thank you! – nicbou Jul 17 '23 at 12:22
2

Set Blob type at Blob constructor instead of at createObjectURL

var blob = new Blob([resultByte], {type: "application/pdf"});
var link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = "myFileName.pdf";
link.click();
guest271314
  • 1
  • 15
  • 104
  • 177
1

As mentioned here, if you're using Axios.get() on the front-end, make sure to add the { responseType: 'blob' } option:

const res = await axios.get("/api/pdf-route", 
    { responseType: 'blob' } // <----- ***
);

And then the download is as many mentioned:

const pdfBlob = new Blob([res.data], { type: 'application/pdf' });
const downloadUrl = URL.createObjectURL(pdfBlob);
const link = document.createElement("a");
link.href = downloadUrl;
link.download = filename;
link.click();
URL.revokeObjectURL(downloadUrl);

For more info: https://stackoverflow.com/a/76126845/12056841

Shani Kehati
  • 427
  • 5
  • 10
0

Easiest way would be converting bytes to the base64 format and construct link as below

let link=document.createElement('a');
const mimeType = "application/pdf";
link.href=`data:${mimeType};base64,${base64Str}`;
link.download="myFileName.pdf";
link.click();

Link can be generated on backend side and retrieved from the response.
File bytes can be read as base64 string in Python as following:

with open("my-file.pdf", "rb") as file:
    base46_str = base64.b64encode(file.read()).decode("utf-8")
Jumshud
  • 1,385
  • 1
  • 13
  • 19