Downloading a PDF using JavaScript that is Cross Browser Compatible

4.1k views Asked by At

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...
            }
        }
    }
1

There are 1 answers

4
dmungin On

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); 
    }
}