javascriptjquerypromisejszip

Wrapping JS promises into a function


I have a web application that collects various files from URLs and puts them together in the zip archive.

I'm using JSZip to work with zip files. Here is a code sample that includes contents of another zip archive located on the same server:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>   
   <script type="text/javascript" src="./js/jszip.js"></script>
    <script type="text/javascript" src="./js/jszip-utils.js"></script>
    <script type="text/javascript" src="./js/FileSaver.js"></script>

Downloading archive with contents from another archive



<script>

jQuery(function($) {

            var zip = new JSZip();
            zip.file('see.txt','Regular files are being included with a single function call');



            function flash(){
            JSZipUtils.getBinaryContent('/version.zip', function(err, data) {
                try {
                  zip.loadAsync(data)
                  .then(function(zip) {
                        zip.generateAsync({type: 'blob'},
                        function(metadata) {
                        })
                        .then(function(blob) {
                            saveAs(blob, 'result.zip');

                        }, function(e) {
                            showError(e);
                        });
                  })
                  .then(function success() {

                  });
                } catch(e) {console.log(e)}
              });
            };


        flash();



        return false;

});

</script>

Working JSFiddle with libs and examples included

It works just fine. JSZipUtils.getBinaryContent returns a promise with binary content of a zip file from the URL. zip.loadAsync() reads binary content and includes it into the archive. zip.generateAsync builds a representation of zip archive in RAM so we can download it later.

However, I need to wrap zip.loadAsync() into a function that I can call multiple times. I've tried something like this:

function zipmerge(source){
JSZipUtils.getBinaryContent(source, function (err, data) {
   if(err) {
      throw err; // or handle the error
   }
   var zip = new JSZip();
   zip.loadAsync(data);
});
};

But it looks like zip.loadAsync() needs a wait in order to be completed. If I just run the function above and then generate a zip file, it will just ignore the contents of zip file I'm trying to incude.

I'm not very good with how promisies work, so I need a help with a function that will recieve an url and wait for the promise to resolve so I could call it multiple times along with regular zip.file and then trigger a zip generation. Thanks.


Solution

  • You can wrap the JSZipUtils.getBinaryContent method in a Promise and generate an array of them. Then, you can use Promise.all() to create a new promise which resolves when all the promises in an array have resolved.

    Here's an example of how I would do it:

    // function to read in a list of source zip files and return a merged archive
    function mergeZips(sources) {
        var zip = new JSZip();
    
        return readSources(sources, zip)
            .then(function() {
                return zip;
            });
    }
    
    // generate an array of promises for each zip we're reading in and combine them
    // into a single promise with Promise.all()
    function readSources(files, zip) {
        return Promise.all(
            files.map(function(file){
                return readSource(file, zip);
            })
        );
    }
    
    // promise-ified wrapper function to read & load a zip
    function readSource(file, zip) {
        return new Promise(function(resolve, reject) {
            JSZipUtils.getBinaryContent(file, function (err, data) {
                if (err) {
                    reject(err);
                }
                // resolving the promise with another promise will pass the promise
                // down the chain:
                resolve(zip.loadAsync(data)); 
            });
        });
    }
    
    // example usage:
    mergeZips([
        'file1.zip',
        'file2.zip',
        'file3.zip'
    ]).then(function(zip) {
        zip.generateAsync({type: 'blob'})
            .then(function(blob){
                saveAs(blob, 'result.zip')
            })
    });