Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 853
westandy Avatar asked Jun 21 '16 15:06

westandy


People also ask

How do I download an entire zip file?

To unzip the entire folder, right-click to select Extract All, and then follow the instructions. To unzip a single file or folder, double-click the zipped folder to open it. Then, drag or copy the item from the zipped folder to a new location.

How do I download a zip file from an FTP server?

Credentials = new NetworkCredential(_remoteUser, _remotePass); FtpWebResponse response = (FtpWebResponse)request. GetResponse(); Stream responseStream = response. GetResponseStream(); StreamReader reader = new StreamReader(responseStream); StreamWriter writer = new StreamWriter(destination); writer.

What is client zip?

client-zip concatenates multiple files (e.g. from multiple HTTP requests) into a single ZIP, in the browser, so you can let your users download all the files in one click. It does not compress the files or unzip existing archives.


1 Answers

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);
like image 68
Bahadir Tasdemir Avatar answered Oct 20 '22 14:10

Bahadir Tasdemir