Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Save binary file from base64 data in Javascript [duplicate]

Tags:

javascript

I am trying to download xlsx spreadsheet with javascript. I have tested base64 data. I decode it like so:

var data = atob(validBase64Data);

After that, I do:

save(name, data, type) {
  const blob = new Blob([data], {type: type});
  let objectURL = window.URL.createObjectURL(blob);
  let anchor = document.createElement('a');

  anchor.href = objectURL;
  anchor.download = name;
  anchor.click();

  URL.revokeObjectURL(objectURL);
}

Where name is a filename.xlsx, data is the decoded data and type is a mime-type string.

The excel file is downloaded but would not open as excel. Data is corrupted somehow.

In addition: I tested the same data with a unix terminal command to base64 decode and write the xlsx directly into that file, and that produced working file. Test was done like so:

  1. I saved base64 data to test_excel.txt`
  2. Ran command base64 -D -i test_excel.txt -o test_excel.xlsx
  3. test_excel.xlsx is recognized by excel.

What am I doing wrong with the code?

like image 760
Lex Avatar asked Feb 16 '18 23:02

Lex


2 Answers

Here is the code that solved it:

export default {
 save(name, data, type, isBinary) {
  if (isBinary) {
    var bytes = new Array(data.length);
    for (var i = 0; i < data.length; i++) {
        bytes[i] = data.charCodeAt(i);
    }
    data = new Uint8Array(bytes);
  }

  var blob = new Blob([data], {type: type});
  let objectURL = window.URL.createObjectURL(blob);
  let anchor = document.createElement('a');

  anchor.href = objectURL;
  anchor.download = name;
  anchor.click();

  URL.revokeObjectURL(objectURL);
 }
}

Thanks to everyone who participated in resolving. Also, credits to: Creating a Blob from a base64 string in JavaScript

like image 74
Lex Avatar answered Sep 30 '22 06:09

Lex


Okay, so let's clarify a few things before anyone tries to "explain" the problem incorrectly.

The original .xlsx is a binary-encoded file, meaning that the data will contain bytes in the full range of 0x00 to 0xFF.

In the question, it is assumed that this string has been successfully encoded into a valid base64 string, with no extraneous characters (as indicated by the success of the test using base64 without the -i flag), and stored to validBase64Data.

The problem is that atob(validBase64Data) generates a string decoded into utf-8, not binary. And as I said before, the original binary string contains non-ASCII bytes in the range 0x80 to 0xFF. In utf-8, these code points are stored as two bytes instead of one, so the solution, as described in Creating a Blob from a base64 string in JavaScript, is to convert the code points of each character in the utf-8 string data into bytes stored as a Uint8Array, and then construct a Blob from that.

A naive solution might look like this, though please refer to Creating a Blob from a base64 string in JavaScript for more performant solutions:

const blob = new Blob([Uint8Array.from(data, c => c.charCodeAt(0))], { type });
//...

This uses TypedArray.from(iterable, mapFn).

like image 28
Patrick Roberts Avatar answered Sep 30 '22 04:09

Patrick Roberts