Monday, August 18, 2014

Angular.JS Service to Download Data Generated in Browser

I ran into a situation where I wanted to be able to download data that was generated in the browser. There are several libraries that handle this scenario, but I thought it should have been simpler than those large libraries.

Below is the result. It supports IE 10+ (potentially 9 haven't tested), FireFox, and Chrome.
angular.module('FileDownload')
.service('SaveFileService', [function () {
        this.Save = function(data, filename, mimeType) {
            var blob = new Blob([data.join('\n')], {type: mimeType});
            if (/\bMSIE\b|\bTrident\b|\bEdge\b/.test(navigator.userAgent)) {
                window.navigator.msSaveOrOpenBlob(blob, filename);
            } else {
                var url  = window.URL || window.webkitURL,
                    link = document.createElementNS("http://www.w3.org/1999/xhtml", "a"),
                    event = document.createEvent("MouseEvents");
                
                link.href = url.createObjectURL(blob);
                link.download = filename;

                event.initEvent("click", true, false);
                link.dispatchEvent(event);
            }
        };
    }]);
Basically, the Save function does a couple things. For IE browsers, it creates the blob and calls the msSaveOrOpenBlob (the msSaveBlob function would make IE only display a save dialog and not give the user the option to open it). For all other browsers, it creates the blob, generates a link element, creates a mouse click event, and has the link dispatch the created event.

To use the service, just inject the service into the controller and call the Save function w/ the needed parameters.

UPDATE 2015-09-28: Per Kevin's feedback, I have updated the regular expression to catch the Edge browser (so much for Microsoft making a standards evergreen browser). I have verified that the above change works as advertised.

2 comments:

Unknown said...

I'm using this in my project, tested it for IE10, IE11, Firefox, Chrome and Opera. It doesn't work anymore for Edge but you just need to change the regex to /\bMSIE\b|\bTrident\b|\bEdge\b/

Brock said...

Kevin: I successfully tested your change in my demo application and updated my example accordingly. Thanks for the feedback!