最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Client download of a server generated zip file - Stack Overflow

programmeradmin1浏览0评论

Before somebody says, "duplicate", I just want to make sure, that folks know, that I have already reviewed these questions:

1) Uses angular and php, not sure what is happening here (I don't know PHP): Download zip file and trigger "save file" dialog from angular method

2) Can't get this answer to do anything: how to download a zip file using angular

3) This person can already download, which is past the point I'm trying to figure out: Download external zip file from angular triggered on a button action

4) No answer for this one: download .zip file from server in nodejs

5) I don't know what language this even is:

Given those questions, if this is still a duplicate, I apologize. Here is, yet, another version of this question.

My angular 1.5.X client gives me a list of titles, of which each have an associated file. My Node 4.X/Express 4.X server takes that list, gets the file locations, creates a zip file, using express-zip from npm, and then streams that file back in the response. I then want my client to initiate the browser's "download a file" option.

Here's my client code (Angular 1.5.X):

function bulkdownload(titles){
    titles = titles || [];
    if ( titles.length > 0 ) {
        $http.get('/query/bulkdownload',{
            params:{titles:titles},
            responseType:'arraybuffer'
        })
        .then(successCb,errorCb)
        .catch(exceptionCb);
    }

    function successCb(response){
        // This is the part I believe I cannot get to work, my code snippet is below
    };

    function errorCb(error){
            alert('Error: ' + JSON.stringify(error));
    };

    function exceptionCb(ex){
            alert('Exception: ' + JSON.stringify(ex));
    };
};

Node (4.X) code with express-zip, :

router.get('/bulkdownload',function(req,resp){
    var titles = req.query.titles || [];

    if ( titles.length > 0 ){
        utils.getFileLocations(titles).
        then(function(files){
            let filename = 'zipfile.zip';

            // .zip sets Content-Type and Content-disposition
            resp.zip(files,filename,console.log);
        },
        _errorCb)
    }
});

Here's my successCb in my client code (Angular 1.5.X):

function successCb(response){
    var URL = $window.URL || $window.webkitURL || $window.mozURL || $window.msURL;
    if ( URL ) {
        var blob = new Blob([response.data],{type:'application/zip'});
        var url = URL.createObjectURL(blob);
        $window.open(url);
    }
};

The "blob" part seems to work fine. Checking it in IE's debugger, it does look like a file stream of octet information. Now, I believe I need to get that blob into the some HTML5 directive, to initiate the "Save File As" from the browser. Maybe? Maybe not?

Since 90%+ of our users are using IE11, I test all of my angular in PhantomJS (Karma) and IE. When I run the code, I get the old "Access is denied" error in an alert window:

Exception: {"description":"Access is denied...<stack trace>}

Suggestions, clarifications, answers, etc. are welcome!

Before somebody says, "duplicate", I just want to make sure, that folks know, that I have already reviewed these questions:

1) Uses angular and php, not sure what is happening here (I don't know PHP): Download zip file and trigger "save file" dialog from angular method

2) Can't get this answer to do anything: how to download a zip file using angular

3) This person can already download, which is past the point I'm trying to figure out: Download external zip file from angular triggered on a button action

4) No answer for this one: download .zip file from server in nodejs

5) I don't know what language this even is: https://stackoverflow.com/questions/35596764/zip-file-download-using-angularjs-directive

Given those questions, if this is still a duplicate, I apologize. Here is, yet, another version of this question.

My angular 1.5.X client gives me a list of titles, of which each have an associated file. My Node 4.X/Express 4.X server takes that list, gets the file locations, creates a zip file, using express-zip from npm, and then streams that file back in the response. I then want my client to initiate the browser's "download a file" option.

Here's my client code (Angular 1.5.X):

function bulkdownload(titles){
    titles = titles || [];
    if ( titles.length > 0 ) {
        $http.get('/query/bulkdownload',{
            params:{titles:titles},
            responseType:'arraybuffer'
        })
        .then(successCb,errorCb)
        .catch(exceptionCb);
    }

    function successCb(response){
        // This is the part I believe I cannot get to work, my code snippet is below
    };

    function errorCb(error){
            alert('Error: ' + JSON.stringify(error));
    };

    function exceptionCb(ex){
            alert('Exception: ' + JSON.stringify(ex));
    };
};

Node (4.X) code with express-zip, https://www.npmjs.com/package/express-zip:

router.get('/bulkdownload',function(req,resp){
    var titles = req.query.titles || [];

    if ( titles.length > 0 ){
        utils.getFileLocations(titles).
        then(function(files){
            let filename = 'zipfile.zip';

            // .zip sets Content-Type and Content-disposition
            resp.zip(files,filename,console.log);
        },
        _errorCb)
    }
});

Here's my successCb in my client code (Angular 1.5.X):

function successCb(response){
    var URL = $window.URL || $window.webkitURL || $window.mozURL || $window.msURL;
    if ( URL ) {
        var blob = new Blob([response.data],{type:'application/zip'});
        var url = URL.createObjectURL(blob);
        $window.open(url);
    }
};

The "blob" part seems to work fine. Checking it in IE's debugger, it does look like a file stream of octet information. Now, I believe I need to get that blob into the some HTML5 directive, to initiate the "Save File As" from the browser. Maybe? Maybe not?

Since 90%+ of our users are using IE11, I test all of my angular in PhantomJS (Karma) and IE. When I run the code, I get the old "Access is denied" error in an alert window:

Exception: {"description":"Access is denied...<stack trace>}

Suggestions, clarifications, answers, etc. are welcome!

Share Improve this question edited May 23, 2017 at 12:02 CommunityBot 11 silver badge asked Jun 21, 2016 at 15:37 westandywestandy 1,4403 gold badges19 silver badges41 bronze badges 6
  • 1 So what happens? At which point does it fail? Do you by chance have a pop-up blocker? – idbehold Commented Jun 21, 2016 at 15:44
  • is there any error? can you download the file if type the url http://localhost/query/bulkdownload?titles=123 manually? – Roland Starke Commented Jun 21, 2016 at 15:47
  • Oh shoot! Sorry, let me update that. – westandy Commented Jun 21, 2016 at 15:55
  • Roland Starke: the browser just seems to spin when I do that, but eventually, yes, it returned a _tmp_zipfile.zip! However, I debugged the server code, and I'm getting the correct array of titles, the file locations, and it is injected into the resp.zip correctly, as far I could tell from a series of console.logs. – westandy Commented Jun 21, 2016 at 16:12
  • 1 maybe ie handles blobs as cross origin? you could skip that whole ajax blob stuff if you just open the url directly like window.open('/query/bulkdownload?title=xxx'); But as idbehold said that maybe is blocked because of pupup blockers. here are some ways to do it: stackoverflow.com/questions/11620698/… – Roland Starke Commented Jun 22, 2016 at 8:17
 |  Show 1 more comment

4 Answers 4

Reset to default 12

Use this one:

var url="YOUR ZIP URL HERE";
window.open(url, '_blank');
var zip_file_path = "" //put inside "" your path with file.zip
var zip_file_name = "" //put inside "" file name or something
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = zip_file_path;
a.download = zip_file_name;
a.click();
document.body.removeChild(a);

As indicated in this answer, I have used the below Javascript function and now I am able to download the byte[] array content successfully.

Function to convert byte array stream (type of string) to blob object:

var b64toBlob = function(b64Data, contentType, sliceSize) {
      contentType = contentType || '';
      sliceSize = sliceSize || 512;

      var byteCharacters = atob(b64Data);
      var byteArrays = [];

      for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        var slice = byteCharacters.slice(offset, offset + sliceSize);

        var byteNumbers = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
          byteNumbers[i] = slice.charCodeAt(i);
        }

        var byteArray = new Uint8Array(byteNumbers);

        byteArrays.push(byteArray);
      }

      var blob = new Blob(byteArrays, {type: contentType});
      return blob;
};

An this is how I call this function and save the blob object with FileSaver.js (getting data via Angular.js $http.get):

    $http.get("your/api/uri").success(function(data, status, headers, config) {
        //Here, data is type of string
        var blob = b64toBlob(data, 'application/zip');
        var fileName = "download.zip";
        saveAs(blob, fileName);
    });

Note: I am sending the byte[] array (Java-Server-Side) like this:

byte[] myByteArray = /*generate your zip file and convert into byte array*/ new byte[]();
return new ResponseEntity<byte[]>(myByteArray , headers, HttpStatus.OK);

I updated my bulkdownload method to use $window.open(...) instead of $http.get(...):

function bulkdownload(titles){
    titles = titles || [];
    if ( titles.length > 0 ) {
        var url = '/query/bulkdownload?';
        var len = titles.length;
        for ( var ii = 0; ii < len; ii++ ) {
            url = url + 'titles=' + titles[ii];
            if ( ii < len-1 ) {
                url = url + '&';
            }
        }
        $window.open(url);
    }
};

I have only tested this in IE11.

发布评论

评论列表(0)

  1. 暂无评论