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:
base64 -D -i test_excel.txt -o test_excel.xlsx
What am I doing wrong with the code?
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
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)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With