— Pixels Commander

[ In English, На русском ]

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;

Demonstration

Project on GitHub

  • Maxim Kachurovskiy

    The canonical way of serving file as an attachment is “Content-Disposition: attachment” header – http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1 though it’s not a solution when you don’t control the server.

    Header also has one more advantage – it prevents some file types e.g. SWF from being launched/embedded which is helpful preventing a bunch of attacks.

  • Anonymous

    Yes, definitely, Content-Disposition is right way to go if you control server side, however it`s always good to have control from JavaScript land. Thanks for remark, I`ll edit article a bit.

  • Александр Иванов

    Для того чтобы избежать открытия нового окна можно добавить вторым параметром ‘_self’ – window.open(sUrl, ‘_self’);

  • Anonymous

    Спасибо, добавил этот пункт в статью.

  • Rick

    Thank you for this code. It works great for Chrome in Windows but with Chrome on iPad the file opens in the browser instead of downloading. Any ideas on how to fix this?

  • Anonymous

    As I know there is no ways to download file from iPad browser. Newest snippet version have alert message in case of such attempts.

    Also seems that you need to modify headers on server side.

    Easiest way to configure headers via Apache is to set Header set Content-Disposition “attachment” for files you want to be downloaded.

    So .htaccess can look like:

    Header set Content-Disposition attachment

  • Rick

    Thanks for the update which informs the iPad user about downloads, it’s very helpful.

  • anil vemula

    Nuvvu devudu samee…..

  • Anonymous

    Just opens another tab in latest Chrome…

  • Anonymous

    Just tried to run example from repository. It works as expected. Lets determine you issue, drop me a message in skype lp_funky.

  • Anonymous

    It works now. Used error on my part. Nice code.

  • http://www.gregory.goltsov.info/ Gregory Goltsov

    Thanks for a great article! However your syntax highlighter seems to be slightly broken: in the last code snipped, the “>” sign is mangled, i.e.:


    .indexOf('chrome') & gt; -1;

  • Anonymous

    Thanks, fixed

  • Adam Simms

    Thank you so much for this!

  • Maxim

    Another way is to create hidden form with action = “url-to-file” and then submit it.

  • http://www.jasaaplikasiandroid.com/ Hedi Herdiana

    Hi, it’s not working in webview cordova of android app.

  • Andrei

    А возможно задать директорию для скачивающегося файла?

  • Anonymous

    Нет, к сожалению в браузерах такая возможность не предусмотрена.

  • sriram s

    Downloading file is working fine.i need to download the file with its folder-javascript

  • G

    Same is not working as chrome extension. I added manifest.json. It opens the html page. Does not display
    label.innerHTML = ‘Download will start in ‘ + secondsBeforeDownloading + ‘ seconds…’;