After much searching around and trying approaches from various places, I was able to come up with the following.
The tryAnchorDownload()
approach should work on modern versions of FireFox and Chrome, and is a cleaned-up version of code provided in this forum post.
The trySaveAsDownload()
approach should theoretically work on any major modern browser, though Safari may not respect the specified file name. It requires the FileSaver.js library and some browsers may need the Blob.js polyfill.
You can call the main function here like this:
initiateFileDownload(bytes, "myFile.txt");
where bytes
is a Uint8Array
of the file's bytes.
function byteArrayToBase64(bytes) {
var chArray = Array.prototype.map.call(bytes,
function (byte) { return String.fromCharCode(byte); });
return window.btoa(chArray.join(""));
}
var octetStreamMimeType = "application/octet-stream";
function tryAnchorDownload(fileBytes, fileName) {
var aElement = document.createElement("a"),
event;
if ("download" in aElement) {
aElement.setAttribute("download", fileName);
aElement.href = "data:" + octetStreamMimeType +
";base64," + byteArrayToBase64(fileBytes);
document.body.appendChild(aElement);
event = document.createEvent("MouseEvents");
event.initMouseEvent("click", true, false, window, 0, 0, 0, 0, 0,
false, false, false, false, 0, null);
aElement.dispatchEvent(event);
document.body.removeChild(aElement);
return true;
}
return false;
}
function trySaveAsDownload(fileBytes, fileName) {
var blob;
if (window.saveAs) {
blob = new Blob([fileBytes], { type: octetStreamMimeType });
saveAs(blob, fileName);
return true;
}
return false;
}
// fileBytes is a Uint8Array
function initiateFileDownload(fileBytes, fileName) {
return tryAnchorDownload(fileBytes, fileName) ||
trySaveAsDownload(fileBytes, fileName);
}
A more thorough (and probably more efficient) alternative to the byteArrayToBase64()
function above can be found on MDN.