Download attribute on A tag not working in IE - html

From the following code I'm creating a dynamic anchor tag which downloads a file. This code works well in Chrome but not in IE. How can I get this working
<div id="divContainer">
<h3>Sample title</h3>
</div>
<button onclick="clicker()">Click me</button>
<script type="text/javascript">
function clicker() {
var anchorTag = document.createElement('a');
anchorTag.href = "http://cdn1.dailymirror.lk/media/images/finance.jpg";
anchorTag.download = "download";
anchorTag.click();
var element = document.getElementById('divContainer');
element.appendChild(anchorTag);
}
</script>

Internet Explorer does not presently support the Download attribute on A tags.
See http://caniuse.com/download and http://status.modern.ie/adownloadattribute; the latter indicates that the feature is "Under consideration" for IE12.

In my case, since there's a requirement to support the usage of IE 11 (version 11.0.9600.18665), I ended up using the solution provided by #Henners on his comment:
// IE10+ : (has Blob, but not a[download] or URL)
if (navigator.msSaveBlob) {
return navigator.msSaveBlob(blob, fileName);
}
It's quite simple and practical.
Apparently, this solution was found on the Javascript download function created by dandavis.

Old question, but thought I'd add our solution. Here is the code I used on my last project. It's not perfect, but it passed QA in all browsers and IE9+.
downloadCSV(data,fileName){
var blob = new Blob([data], {type: "text/plain;charset=utf-8;"});
var anchor = angular.element('<a/>');
if (window.navigator.msSaveBlob) { // IE
window.navigator.msSaveOrOpenBlob(blob, fileName)
} else if (navigator.userAgent.search("Firefox") !== -1) { // Firefox
anchor.css({display: 'none'});
angular.element(document.body).append(anchor);
anchor.attr({
href: 'data:attachment/csv;charset=utf-8,' + encodeURIComponent(data),
target: '_blank',
download: fileName
})[0].click();
anchor.remove();
} else { // Chrome
anchor.attr({
href: URL.createObjectURL(blob),
target: '_blank',
download: fileName
})[0].click();
}
}
Using the ms specific API worked best for us in IE. Also note that some browsers require the anchor to actually be in the DOM for the download attribute to work, whereas Chrome, for example, does not. Also, we found some inconsistencies with how Blobs work in various browsers. Some browsers also have an export limit. This allows the largest possible CSV export in each browser afaik.

As of build 10547+, the Microsoft Edge browser is now supporting the download attribute on a tags.
Download Image
Edge features update: https://dev.windows.com/en-us/microsoft-edge/platform/changelog/desktop/10547/
a[download] standard: http://www.w3.org/html/wg/drafts/html/master/links.html#attr-hyperlink-download

This code fragment allows saving blob in the file in IE, Edge and other modern browsers.
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
// Extract filename form response using regex
var filename = "";
var disposition = request.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
}
if (window.navigator.msSaveOrOpenBlob) { // for IE and Edge
window.navigator.msSaveBlob(request.response, filename);
} else {
// for modern browsers
var a = document.createElement('a');
a.href = window.URL.createObjectURL(request.response);
a.download = filename;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
}
}
button.disabled = false;
dragArea.removeAttribute('spinner-visible');
// spinner.style.display = "none";
};
request.open("POST", "download");
request.responseType = 'blob';
request.send(formData);
For IE and Edge use: msSaveBlob

Use my function
It bind your atag to download file in IE
function MS_bindDownload(el) {
if(el === undefined){
throw Error('I need element parameter.');
}
if(el.href === ''){
throw Error('The element has no href value.');
}
var filename = el.getAttribute('download');
if (filename === null || filename === ''){
var tmp = el.href.split('/');
filename = tmp[tmp.length-1];
}
el.addEventListener('click', function (evt) {
evt.preventDefault();
var xhr = new XMLHttpRequest();
xhr.onloadstart = function () {
xhr.responseType = 'blob';
};
xhr.onload = function () {
navigator.msSaveOrOpenBlob(xhr.response, filename);
};
xhr.open("GET", el.href, true);
xhr.send();
})
}

Append child first and then click
Or you can use window.location= 'url' ;

As mentioned in earlier answer , download attribute is not supported in IE . As a work around, you can use iFrames to download the file . Here is a sample code snippet.
function downloadFile(url){
var oIframe = window.document.createElement('iframe');
var $body = jQuery(document.body);
var $oIframe = jQuery(oIframe).attr({
src: url,
style: 'display:none'
});
$body.append($oIframe);
}

I copied the code from here and updated it for ES6 and ESLint and added it to my project.
You can save the code to download.js and use it in your project like this:
import Download from './download'
Download('/somefile.png', 'somefile.png')
Note that it supports dataURLs (from canvas objects), and more... see https://github.com/rndme for details.

Related

JsPDF - Not allowed to navigate top frame to data URL

After updating Google Chrome, the report jsPDF in a new Window does not work any more.
The console shows the message:
Not allowed to navigate top frame to data URL:
data:application/pdf;base64,JVBERi0xLjMKMyAwIG9iago8PC9UeXBlIC9QYWdlCi9QYXJlbnQgMSAwIFIKL1....
Can you help-me?
Thanks.
This works well now that chrome has removed top frame navigation. Only downloading the pdf in chrome gives problem. Download works in well in firefox tho.
var string = doc.output('datauristring');
var iframe = "<iframe width='100%' height='100%' src='" + string + "'></iframe>"
var x = window.open();
x.document.open();
x.document.write(iframe);
x.document.close();
Apparently Google Chrome has removed support for top-frame navigation, you can see more informations here: https://groups.google.com/a/chromium.org/forum/#!topic/blink-dev/GbVcuwg_QjM
You may try to render the jsPDF to an iFrame
I recently had the same problem using FileReader object to read content and show my JSReport.
var reader = new FileReader();
reader.onload = function (e) {
window.open(reader.result, "_blank");
}
reader.readAsDataURL(blob);
Unfortunatly after chrome update all my report stopped working.
I tried to fix this by using Blob object and it's still working but if you have a popup blocker it will not work.
var file = new Blob([blob], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
I finally find a way to avoid this problem by creating the iFrame dynamically after reading this topic and i decided to share the solution.
var file = new Blob([blob], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
var win = window.open();
win.document.write('<iframe src="' + fileURL + '" frameborder="0" style="border:0; top:0px; left:0px; bottom:0px; right:0px; width:100%; height:100%;" allowfullscreen></iframe>')
Maybe can help, create a function to export with the download attribute html5:
var docPdf = doc.output();
exportToFile(docPdf,defaults.type);
function exportToFile(data,type){
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/'+type+';filename='+'exportar.'+type+';'+'charset=utf-8,' + encodeURI(data);
hiddenElement.target = '_blank';
hiddenElement.download = 'exportar.'+type;
hiddenElement.click();
}
the download property of the a element has to contain a file name.
function window_download( datauri )
{
const match = datauri.match(/(filename=)([^;]+)/);
const fileName = match ? match[2] : '' ;
if ( fileName )
{
const downloadLink = document.createElement("a");
downloadLink.download = fileName;
downloadLink.innerHTML = "Download File";
downloadLink.href = datauri ;
downloadLink.click();
}
else
{
throw 'missing download file name' ;
}
// get contents of pdf from jsPDF as a data uri.
const uri = pdf.output('datauristring', 'packing-list.pdf' );
window_download( uri ) ;
<iframe id="ManualFrame"
frameborder="0"
style="border:0"
allowfullscreen>
</iframe>
<script>
$(function () {
setManualFrame();
});
function setManualFrame() {
$("#ManualFrame").attr("height", screen.height);
$("#ManualFrame").attr("width", screen.width);
$("#ManualFrame").attr("src", "data:application/pdf;base64," + Your_PDF_Data);
}
</script>
var pdfUrl = doc.output('datauri').substring(doc.output('datauri').indexOf(',')+1);
var binary = atob(pdfUrl.replace(/\s/g, ''));
var len = binary.length;
var buffer = new ArrayBuffer(len);
var view = new Uint8Array(buffer);
for (var i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i);
}
var blob = new Blob( [view], { type: "application/pdf" });
var url = URL.createObjectURL(blob);
function openPDF(){
window.open(url);
}
As chrome removed its support for - Top-frame navigation. Luckily jsPDF has an API - "save()", which offers the same functionality as doc.output('datauri')
Below is the example implementation of save()
var doc = new jsPDF();
doc.addImage(imgData, 'JPEG', 0, 0, 300, 160);
doc.save('fileName.pdf');
save(filename, options) → {jsPDF|Promise}
Saves as PDF document. An alias of jsPDF.output('save', 'filename.pdf'). Uses FileSaver.js-method saveAs.
Refer JSPDF documentation for more information -
Add attrbute download
Based on Joyston's answer, but without reparsing and therefore without additional need to escape something:
x=window.open();
iframe=x.document.createElement('iframe')
iframe.width='100%'
iframe.height='100%'
iframe.frameBorder=0
iframe.style="border: 0"
iframe.src='data:text/html........' //data-uri content here
x.document.body.appendChild(iframe);
Not related to jspdf, but did help me here (and this question is the top hit at google): If specifying a download="..." attribute to the anchor tag, the download prompt will properly open up.
#kuldeep-choudhary
Hi, in fact, to solve i'm using object tag with angularJS 1.5.xx
<object ng-attr-data="{{data}}" type="application/pdf"></object>
and in script:
$scope.doc.data = $sce.trustAsResourceUrl(doc.output("datauristring"));
In pure javascript, maybe like this works:
html:
<object id="obj" type="application/pdf" ></object>
js:
document.elementById('obj').data = doc.output("datauristring");
please, try and correct-me if I wrong.
Using download="" made me able to download the file
/**
* Creates an anchor element `<a></a>` with
* the base64 pdf source and a filename with the
* HTML5 `download` attribute then clicks on it.
* #param {string} pdf
* #return {void}
*/
function downloadPDF(pdf) {
const linkSource = `data:application/pdf;base64,${pdf}`;
const downloadLink = document.createElement("a");
const fileName = "vct_illustration.pdf";
downloadLink.href = linkSource;
downloadLink.download = fileName;
downloadLink.click();
}
Source from: https://medium.com/octopus-labs-london/downloading-a-base-64-pdf-from-an-api-request-in-javascript-6b4c603515eb
In angular2+ -
app.component.html -
<object id="obj" [attr.data] type="application/pdf"> </object>
app.component.ts
document.getElementById('obj').dataset.data = doc.output("datauristring");
var blob = doc.output("blob");
window.open(URL.createObjectURL(blob));

HTML <a> tag download file error handling

In my app I have tag with link to api for file download (pdf). The problem is that it is not 100% stable and I have to handle then service is not available or file is not available and server responds with error.
<a href="link/to/api" target="_blank" download="filename">
By the way I am using AngularJS in this app. If there is any solution using it it would help a lot
In case somebody else will face similar problem. Here is the solution I have implemented after some research.
Remove the link from an <a> tag and add a click event:
<a href="#" ng-click="downloadFile()">
now you need to download blob (here you can control if you can access it file) and let make DOM object add all needed attributes end trigger it.
$scope.downloadFile = function () {
$http.get('api/link', { responseType: 'arraybuffer' })
.then(function (data) {
var file = new Blob([data], { type: "application/pdf" });
var url = $window.URL || $window.webkitURL;
var fileURL = url.createObjectURL(file);
var a = document.createElement("a");
a.href = fileURL;
a.download = "nameOfFile";
a.target = "_self";
a.click();
url.revokeObjectURL(fileURL);
}).error(function (data) {
console.error(data);
})
};
UPDATE:
This was working only for Chrome. Other browsers had different approach of downloading blob. So I have users FileSaver.js for this task. Even then I had problems opening it on iOS. It is blocking file saving if it was triggered out side of user event. Here is my workaround for this.
var file = new Blob([data], { type: "application/pdf" });
var isIos = (navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false);
if(isIos){
var element = document.getElementById("downloadButton");
element.onclick - function(){
saveAs(file, "name.pdf");
}
element.onclick();
} else {
saveAs(file, "name.pdf");
}
Hope this will save time for someone.
Use extensionnames after the link like link/to/api.pdf and download="filename.pdf".
Also, try target="_self"

IE 9+ Download Attribute workaround

I am trying to download a file from my web service. I need to pass complex meta data to the server to know how to download the file. Here is how Im able to accomplish that in evergreen browsers:
// i use angular but not important for this demo
$http.post({ /* complex object */ }).then(xhr){
// use download attribute
// http://davidwalsh.name/download-attribute
var hiddenElement = document.createElement('a');
hiddenElement.href = 'data:attachment/csv,' + encodeURI(xhr.data);
hiddenElement.target = '_blank';
hiddenElement.download = $scope.filename + '.csv';
hiddenElement.click();
hiddenElement.remove();
});
of course sense the download attribute is not available on IE I'm not able to post. A workaround I've used before is:
$("body>#download_iFrame").remove();
$("body").append('<iframe name="downloadFrame" id="download_iFrame" style="display:none;" src="" />');
$("#form-download")[0].submit();
and then in html
<form target="downloadFrame"
action="'api/search/export/'"
id="form-download"></form>
problem is I can't pass a object like that. Sure I can put a hidden input and serialize its value but my object is kinda big so that ends up being a problem.
How do you work around this?
If you're only concerned about recent browsers you might take a look at using FileSaver.js. When running on IE10+ it uses navigator.msSaveOrOpenBlob.
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "blob";
xhr.onload = fuction (eventInfo) {
if (this.status == 200) {
var blob = this.response;
// FileSaver.js usage:
saveAs(blob, "filename.ext");
// Or IE10+ specific:
navigator.msSaveOrOpenBlob(blob, "filename.ext");
}
};
xhr.send();

Auto Pop Up Save As Window For Firefox

I've been using this code download PDF to directly download a link for pdf but this code only work in Google Chrome
Can someone help me to make this work in Mozilla Firefox and IE8 as well. Because when I try to test it in Mozilla Firefox it opens the links but doesn't pop up the "Save As" window.
I'd use PHP headers so reference the file with something like this for the url:
download PDF
Then use php to deliver the file:
<?php
/* put some validation and injection protection here */
$approvedFiles = ["myFile.pdf","myOtherFile.pdf"];
if (!in_array($_GET['file'],$approvedFiles)){
header("HTTP/1.1 404 Not Found");
die();
}
//otherwise
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="myFile.pdf"');
readfile('myFile.pdf');
Note: pseudo code is not perfect :)
Refs: http://php.net/manual/en/function.header.php
http://jsfiddle.net/RemMp/
<a id="save_data" href="">link</a>
window.URL = window.webkitURL || window.URL;
var a = document.getElementById('save_data');
var data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==';
var filename = 'qqq.pdf';
a.download = filename;
a.href = data;
a.textContent = 'Downloading...';
//release memory
a.onclick = function(e){
save_cleanup(this);
};
//force click
document.querySelector('#save_data').click();
function save_cleanup(a){
a.textContent = 'Downloaded';
setTimeout(function(){
a.href = '';
var element = document.getElementById("save_data");
element.parentNode.removeChild(element);
}, 1500);
};

Filename of downloaded file in data:Application/octet-stream;

I am trying to download a file using data uri in following manner:
<input type="button"
onclick="window.location.href='data:Application/octet-stream;content-disposition:attachment;filename=file.txt,${details}'"
value="Download"/>
The problem is that the downloaded file is always named 'Unknown', whatever I try to use as
filename. Is this the correct way to give the file a name ? or something else needs to be
done ?
Here's the solution, you just have to add a download attribute to anchor tag
a with desired name
<a href="data:application/csv;charset=utf-8,Col1%2CCol2%2CCol3%0AVal1%2CVal2%2CVal3%0AVal11%2CVal22%2CVal33%0AVal111%2CVal222%2CVal333"
download="somedata.csv">Example</a>
Another solution is to use JQuery/Javascript
Anchor's Download Property
On Safari, you might want to use this, and instruct the user to ⌘-S the file:
window.open('data:text/csv;base64,' + encodeURI($window.btoa(content)));
Otherwise, this uses Filesaver.js, but works ok:
var downloadFile = function downloadFile(content, filename) {
var supportsDownloadAttribute = 'download' in document.createElement('a');
if(supportsDownloadAttribute) {
var link = angular.element('<a/>');
link.attr({
href: 'data:attachment/csv;base64,' + encodeURI($window.btoa(content)),
target: '_blank',
download: filename
})[0].click();
$timeout(function() {
link.remove();
}, 50);
} else if(typeof safari !== 'undefined') {
window.open('data:attachment/csv;charset=utf-8,' + encodeURI(content));
} else {
var blob = new Blob([content], {type: "text/plain;charset=utf-8"});
saveAs(blob, filename);
}
}
Note: There is some AngularJS in the code above, but it should be easy to factor out...
I had the same issue and finally I solved in all browsers serving the CSV file in the server-side:
const result = json2csv({ data });
res.writeHead(200
'Content-Type': 'application/octet-stream',
'Content-Disposition': 'attachment;filename=issues.csv',
'Content-Length': result.length
});
res.end(result);
For those that are using other libraries like angularjs or backbone, you can try something like this.
$('a.download').attr('href', 'data:application/csv;charset=utf-8,'+$scope.data);
For anybody looking for a client-side solution using Javascript only, here is mine, working on any browser except IE 10 and lower (and Edge...why?!):
var uri = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(csv);
var link = document.createElement('a');
link.setAttribute("download", "extract.csv");
link.setAttribute("href", uri);
document.body.appendChild(link);
link.click();
body.removeChild(body.lastChild);