简体   繁体   中英

Client download of a server generated zip file

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!

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

Use this one:

var url="YOUR ZIP URL HERE";
window.open(url, '_blank');

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM