AngularJs响应动态下载



我已经根据Scott的回答编写了一个指令。你会这样使用它:

<button class="btn btn-success"
        download-response="getData()"
        download-success="getDataSuccess()"
        download-error="getDataError()"
        download-name="{{name}}.pdf"
        download-backup-url="/Backup/File.pdf">
    Save
</button>

问题:下面的代码在第一个方法的IE11中抛出错误TypeError: Invalid calling object(行:saveBlob(blob, filename);)。尽管它可以追溯到其他下载方法,但我的理解是saveMethod1应该在IE11中工作。

这是代码:

'use strict';
// directive allows to provide a function to be executed to get data to be downloaded
// attributes:
// download-response - Required. Function to get data. It must return a promise. It must be declared on the $scope.
// download-success - Optional. Function to be executed if download-response function was successfully resolved. It must be declared on the $scope.
// download-error - Optional. Function to be executed if download-response function return a promise rejection. It must be declared on the $scope.
// download-mime - Optional. provide a mime type of data being downloaded. Defaulted to "application/octet-stream"
// download-name - Optional. name of the file to download. Defaulted to "download.bin"
// download-backup-url - in case browser does not support dynamic download, this url will be called to get the file
angular.module('app.directives')
    .directive('downloadResponse', [ '$parse', '$timeout',
        function ($parse, $timeout) {
            function saveMethod1(data, filename, contentType) {
                // Support for saveBlob method (Currently only implemented in Internet Explorer as msSaveBlob, other extension in case of future adoption)
                var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
                if (saveBlob) {
                    // Save blob is supported, so get the blob as it's contentType and call save.
                    var blob = new Blob([data], { type: contentType });
                    saveBlob(blob, filename);
                    //console.log("SaveBlob Success");
                } else {
                    throw 'saveBlob is not supported. Falling back to the next method';
                }
            }
            function saveMethod2(data, filename, contentType, octetStreamMime) {
                // Get the blob url creator
                var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
                if (urlCreator) {
                    // Try to use a download link
                    var link = document.createElement("a");
                    var url;
                    if ("download" in link) {
                        // Prepare a blob URL
                        var blob = new Blob([data], { type: contentType });
                        url = urlCreator.createObjectURL(blob);
                        link.setAttribute("href", url);
                        // Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
                        link.setAttribute("download", filename);
                        // Simulate clicking the download link
                        var event = document.createEvent('MouseEvents');
                        event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
                        link.dispatchEvent(event);
                        //console.log("Download link Success");
                    } else {
                        // Prepare a blob URL
                        // Use application/octet-stream when using window.location to force download
                        var blob = new Blob([data], { type: octetStreamMime });
                        url = urlCreator.createObjectURL(blob);
                        window.location = url;
                        //console.log("window.location Success");
                    }
                } else {
                    throw 'UrlCreator not supported. Falling back to the next method';
                }
            }
            function saveMethod3(attrs) {
                if (attrs.downloadBackupUrl && attrs.downloadBackupUrl != '') {
                    console.log('opening ' + attrs.downloadBackupUrl);
                    window.open('http://' + document.domain + attrs.downloadBackupUrl, '_blank');
                } else {
                    throw 'Could not download a file using any of the available methods. Also you did not provide a backup download link. No more bullets left...';
                }
            }
            return {
                restrict: 'A',
                scope: false,
                link:function (scope, elm, attrs) {
                    var getDataHandler = $parse(attrs.downloadResponse);
                    elm.on('click', function() {
                        var promise = getDataHandler(scope);
                        promise.then(
                            function (data) {
                                if (attrs.downloadSuccess && attrs.downloadSuccess != '') {
                                    var successHandler = $parse(attrs.downloadSuccess);
                                    successHandler(scope);
                                }
                                var octetStreamMime = "application/octet-stream";
                                var filename = attrs.downloadName || "download.bin";
                                var contentType = attrs.downloadMime || octetStreamMime;

                                try {
                                    saveMethod1(data, filename, contentType);
                                    return;
                                } catch (e) {
                                    console.log(e);
                                    try {
                                        saveMethod2(data, filename, contentType, octetStreamMime);
                                        return;
                                    } catch (e) {
                                        console.log(e);
                                        try {
                                            saveMethod3(attrs);
                                            return;
                                        } catch (e) {
                                            throw e;
                                        } 
                                        throw e;
                                    } 
                                    throw e;
                                }
                            },
                            function(data) {
                                if (attrs.downloadError && attrs.downloadError != '') {
                                    var errorHandler = $parse(attrs.downloadError);
                                    errorHandler(scope);
                                }
                            }
                        );
                    });
                }
            };
        }
    ]);

非常感谢您的帮助!

问题:

我已经解决了这个问题。Internet Explorer 11似乎不喜欢msSaveBlob函数的别名,最简单的例子就是:

// Succeeds
navigator.msSaveBlob(new Blob(["Hello World"]), "test.txt");
// Fails with "Invalid calling object"
var saveBlob = navigator.msSaveBlob;
saveBlob(new Blob(["Hello World"]), "test.txt");

因此,本质上,为封装saveBlob功能而创建的通用别名(这应该是允许的)阻止了msSaveBlob按预期工作:

var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;

解决方法:

因此,我们的工作是分别对msSaveBlob进行测试。

if(navigator.msSaveBlob)
    navigator.msSaveBlob(blob, filename);
else {
    // Try using other saveBlob implementations, if available
    var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
    if(saveBlob === undefined) throw "Not supported";
    saveBlob(blob, filename);
}

tl;dr

所以msSaveBlob方法是有效的,只要你不别名函数。也许这是一种安全预防措施-,但也许安全异常会更合适,尽管考虑到来源,我认为这很可能是一个缺陷。:)

最新更新