Better approach to download file in JavaScript
Download file in JavaScript – what can be easier? However there are some pitfalls and there is room for improvements. This article describes how to create the best download function in JavaScript and why it`s so good.
If you don`t like to dive deep into theory – you can just get library from GitHub and use downloadFile global function in your project.
Generally there are two file downloading techniques in HTML/JS: window.open and mouse click / tap on link. Both of this methods are not ideal. During investigation of the question some interesting solutions were found. Together they seems to be perfect solution for JavaScript files downloading.
Let`s start development from declaring simple function based on window.open method:
window.downloadFile = function(sUrl) { window.open(sUrl); }
This function is simple and works everywhere, but have some disadvantages:
- We`ll get useless empty window in Chrome or Safari;
- Probably file`s content-type will command browser to show file`s content in new window and not to download it. It`s not expected behavior for downloading function.
How can we avoid this?
“_self” argument for window.open
We can avoid annoying new window opening by adding second argument to window.open:
window.downloadFile = function(sUrl) { window.open(sUrl, '_self'); }
Virtual link and virtual click
Click on link method also have not such problem as empty window in Chrome or Safari. Bad thing is that it`s user-generated event. However we can create hidden link and programmatically click on it by dispatching new mouse event.
Let`s add virtual click code for Chrome and Safari without explanation of browser detection part:
if (window.downloadFile.isChrome || window.downloadFile.isSafari) { //Creating new link node. var link = document.createElement('a'); link.href = sUrl; //Dispatching click event. if (document.createEvent) { var e = document.createEvent('MouseEvents'); e.initEvent('click' ,true ,true); link.dispatchEvent(e); return true; } }
HTML5 “download” attribute and content-type ignoring
What else can annoy user? For example if he tries to download HTML or PNG file and it will be opened in new browser window. I don`t like this behavior, really. And it can be avoided by using HTML5 download attribute. This attribute will tell browser that virtual link we created is aimed for download only. It will download file from link`s href to file with name specified as download attribute`s value. Sad that this great feature works in Chrome only, but 35% of happy users are serious reason to add 5 more lines of code.
Complete listing for download.js:
window.downloadFile = function(sUrl) { //If in Chrome or Safari - download via virtual link click if (window.downloadFile.isChrome || window.downloadFile.isSafari) { //Creating new link node. var link = document.createElement('a'); link.href = sUrl; if (link.download !== undefined){ //Set HTML5 download attribute. This will prevent file from opening if supported. var fileName = sUrl.substring(sUrl.lastIndexOf('/') + 1, sUrl.length); link.download = fileName; } //Dispatching click event. if (document.createEvent) { var e = document.createEvent('MouseEvents'); e.initEvent('click' ,true ,true); link.dispatchEvent(e); return true; } } // Force file download (whether supported by server). var query = '?download'; window.open(sUrl + query); } window.downloadFile.isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; window.downloadFile.isSafari = navigator.userAgent.toLowerCase().indexOf('safari') > -1;