I can download a PDF using AngularJS in Chrome, but this doesn't appear to work in the latest FireFox, Internet Explorer 11 or Edge (assuming it doesn't work for IE10 either), and I know a shim is needed for IE9. Don't know if this the best shim for this if anyone has an opinion, but currently it doesn't seem to work. I tried it with a response type of blob and arraybuffer just in case that made a difference, and it doesn't.
All this counters what caniuse indicates about using the Blob URLs. Anyone have this working in IE9 and up, and the last couple versions of FF, and can point out what I'm doing wrong?
$http({
url: '/api/v1/download',
method: 'GET',
responseType: 'blob' // or 'arraybuffer'
}).then(function (response) {
// Use the Blob object to create an object URL to download the file
var url = URL.createObjectURL(response.data);
// var url = URL.createObjectURL(new Blob([response], {type: 'application/pdf'})); // arraybuffer version
// Create an anchor to perform download, but don't append to the DOM
anchor.href = downloadUrl;
anchor.download = filename;
anchor.target = '_blank';
anchor.click();
URL.revokeObjectURL(downloadUrl);
anchor = null;
}).catch(function (reason) {
console.log('FAIL', reason);
});
UPDATE
Currently the best (only) answer works for IE10, 11, Edge, FF, and continues to work with Chrome. IE9 won't work using this solution if anyone has another polyfill/shim/other/etc, and Safari doesn't support the download attribute so the solution in the chosen answer doesn't work in an SPA since it just redirects the current page so in both these cases I've just left TODO stubs.
This is an update to the posted answer with more information added in comments for anyone to use or hopefully add to so IE9 and Safari work as expected:
function performDownload(blob, filename) {
// IE9 has no API for handling downloads using Blob objects, and doesn't support the download attribute
if(isIE() == 9) {
// TODO: polyfill/shim/other... change response type to?
}
// Only works for IE10 and up, including Edge
else if (typeof window.navigator.msSaveBlob !== 'undefined') {
// Provides a prompt to save the file to a location of users choice
window.navigator.msSaveBlob(blob, filename);
}
// Browsers that adhere to current standards can implement downloads
// using the Blob object with the download anchor attribute
// ---
// NOTE: Edge 13+ is compliant with both these standards, but Edge 12
// does not support the download anchor attribute so all versions
// have been grouped to use the propriety `msSaveBlob` method
else {
// Use the Blob object to create an object URL to download the file
var URL = window.URL;
var downloadUrl = URL.createObjectURL(blob);
var anchor = document.createElement('a');
if(angular.isDefined(anchor.download)) {
anchor.href = downloadUrl;
anchor.download = filename;
anchor.target = '_blank';
document.body.appendChild(anchor); // Required by Firefox
anchor.click();
// Release the existing object URL, and the anchor
$timeout(function () {
URL.revokeObjectURL(downloadUrl);
document.body.removeChild(anchor);
anchor = null;
}, 100);
}
else {
// TODO: Safari does not support the download anchor attribute...
}
}
}
I've used this with success in both IE11 and Chrome:
function saveBlob(response, contentType, filename) {
let blob = new Blob([response.arrayBuffer()], { type: contentType });
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// IE workaround
window.navigator.msSaveBlob(blob, filename);
} else {
let URL = window.URL;
let downloadUrl = URL.createObjectURL(blob);
if (filename) {
let a = document.createElement('a');
if (typeof a.download === 'undefined') {
window.location.href = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
} else {
window.location.href = downloadUrl;
}
// cleanup
setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100);
}
}
Related
code written in javascript to download the data in CSV. which is working fine in FF, Chrome browser. But in Safari showing Type Error
"TypeError: '[object BlobConstructor]' is not a constructor (evaluating 'new Blob([csv], { type: 'text/csv;charset=utf-8;' })')"
I tried in all forum to solve this issue. but nothing work out.
Code I followed :
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
if (navigator.msSaveBlob) { // IE 10+
navigator.msSaveBlob(blob, filename);
} else {
const link = document.createElement('a');
if (link.download !== undefined) { // feature detection
// Browsers that support HTML5 download attribute
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style = 'visibility:hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
Suggestions and solutions welcome.
Downloaded the file in Safari the file name is "Unknown"
Here is my download code
function downloadCSV(csv, filename) {
var downloadContainer = angular.element(''),
content = 'data:attachment/csv,' + encodeURI(csv);
// var downloadLink = angular.element('<a></a>'),
var downloadLink = angular.element(downloadContainer.children()[0]),
blob = new Blob([csv], {
type: 'text/csv;charset=utf-8;'
});
if (window.navigator.msSaveOrOpenBlob) {
// download file for IE
navigator.msSaveBlob(blob, filename);
}
else {
downloadLink.attr('href', content);
downloadLink.attr('download', filename);
downloadLink.attr('style', 'display: none');
// workaround for Safari version that not supporting download feature.
downloadLink.attr('target', '_blank');
angular.element(document).find('body').append(downloadContainer);
// need timeout here since appending DOM takes some time
$timeout(function() {
downloadLink[0].click();
downloadLink.remove();
}, 10);
}
}
and call this function with
downloadCSV(cvs, 'myDownloadFile.csv')
But the downloading the file using Safari is "Unknown" file. Is there any workaround this issue.
BTW - what version of Safari does not support download feature.
Thanks
Safari version < 9.X didn't support download the file.
Code to download the file
function downloadCSV(csv, filename) {
var downloadContainer = angular.element('<div data-tap-disabled="true"><a></a></div>');
// var downloadLink = angular.element('<a></a>'),
var downloadLink = angular.element(downloadContainer.children()[0]),
blob = new Blob([csv], {
type: 'text/csv;charset=utf-8;'
});
if (window.navigator.msSaveOrOpenBlob) {
// download file for IE
navigator.msSaveBlob(blob, filename);
}
else {
downloadLink.attr('href', window.URL.createObjectURL(blob));
downloadLink.attr('download', filename);
downloadLink.attr('style', 'display: none');
angular.element(document).find('body').append(downloadContainer);
// need timeout here since appending DOM takes some time
$timeout(function() {
downloadLink[0].click();
downloadLink.remove();
}, 10);
}
}
Our application to allow the admin to download the file and it works for all browser but not Safari 9.1.2(11601.7.7).Is there any workaround to make in download attribute to make download feature works for Safari?
Thanks
Kim
I'm working on a project where we have an AJAX call which responds with a blob containing an excel file. I want the code to open the file as a download when I get the response. Here is the callback:
var blob = new Blob([response.data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
var objectUrl = URL.createObjectURL(blob);
var a = document.createElement("a");
var header = response.headers("Content-Disposition");
a.download = header.substring(header.indexOf("filename=") + "filename=".length);
a.href = objectUrl;
document.body.appendChild(a);
console.debug("Clicking a tag");
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(objectUrl);
This code works fine in chrome but in firefox nothing happens when a.click() fires. The debug statement prints so I know that the callback is happening. Also, for some reason, if I put a breakpoint on a.click() it works perfectly.
Can someone explain why the click only works in debug mode?
Firefox has a couple of safeguards or just odd behaviors around things like this. I don't know the derivation, but yielding back to the browser for a moment before doing the click usually clears it up:
// ...
a.href = objectUrl;
document.body.appendChild(a);
setTimeout(function() {
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(objectUrl);
}, 0);
// ...
Note that code following the setTimeout will run before the content of the setTimeout does.
You might even need two:
// ...
a.href = objectUrl;
document.body.appendChild(a);
setTimeout(function() {
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(objectUrl);
}, 0);
}, 0);
// ...
I need to be able to save a string into a local file. Based on the code in here I got the following going:
function saveTextAsFile(fileNameToSaveAs, textToWrite) {
var textFileAsBlob = new Blob([textToWrite], {
type: 'text/plain'
});
var downloadLink = document.createElement("a");
downloadLink.download = fileNameToSaveAs;
downloadLink.innerHTML = "Download File";
if (true) { //window.webkitURL !== null) {
// Chrome allows the link to be clicked
// without actually adding it to the DOM.
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
} else {
// Firefox requires the link to be added to the DOM
// before it can be clicked.
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.onclick = destroyClickedElement;
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
}
downloadLink.click();
}
This works fine for Chrome and Firefox, but not for Internet Explorer 10 as
downloadLink.click();
gives:
SCRIPT5: Access is denied.
Is there any explanation/solution to this ?
thanks!
IE 10 and 11 use a distinct syntax for downloading or saving blobs to the client machine. Once you've created a blob, use:
window.navigator.msSaveBlob(blob, 'file.txt');
or
window.navigator.msSaveOrOpenBlob(blob, 'file.txt');
to trigger the file save or file save/open dialog.
For more info, see http://msdn.microsoft.com/en-us/library/ie/hh673542(v=vs.85).aspx
Thx to mstubna, here is a solution for Chrome, FF, and IE>9:
function saveTextAsFile(fileNameToSaveAs, textToWrite) {
/* Saves a text string as a blob file*/
var ie = navigator.userAgent.match(/MSIE\s([\d.]+)/),
ie11 = navigator.userAgent.match(/Trident\/7.0/) && navigator.userAgent.match(/rv:11/),
ieEDGE = navigator.userAgent.match(/Edge/g),
ieVer=(ie ? ie[1] : (ie11 ? 11 : (ieEDGE ? 12 : -1)));
if (ie && ieVer<10) {
console.log("No blobs on IE ver<10");
return;
}
var textFileAsBlob = new Blob([textToWrite], {
type: 'text/plain'
});
if (ieVer>-1) {
window.navigator.msSaveBlob(textFileAsBlob, fileNameToSaveAs);
} else {
var downloadLink = document.createElement("a");
downloadLink.download = fileNameToSaveAs;
downloadLink.href = window.URL.createObjectURL(textFileAsBlob);
downloadLink.onclick = function(e) { document.body.removeChild(e.target); };
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
downloadLink.click();
}
}
For modern browsers solution goes like this, tested: IE11, FF & Chrome
var csvData = new Blob([arg.data], {type: 'text/csv;charset=utf-8;'});
//IE11 & Edge
if (navigator.msSaveBlob) {
navigator.msSaveBlob(csvData, exportFilename);
} else {
//In FF link must be added to DOM to be clicked
var link = document.createElement('a');
link.href = window.URL.createObjectURL(csvData);
link.setAttribute('download', exportFilename);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
plus for ie EDGE:
var ieEDGE = navigator.userAgent.match(/Edge/g);
if (ie || ie11 || ieEDGE) {
if (ieVer>9 || ieEDGE) {
var textFileAsBlob = new Blob([textToWrite], {
type: 'text/plain'
});
window.navigator.msSaveBlob(textFileAsBlob, fileName);
} else {
console.log("No supported on IE ver<10");
}
} else { ... }
You can use FileSaver library to achieve that. Easy to use, takes care of browser types and versions.
There is an AngularJS version available as well.