Search code examples
javascriptperformanceasynchronousjszip

Wait for all files to update, then download them


I'm using the library JSZip to create .png files and add them to a zip archive to be downloaded. The problem comes when I add too many large files.

I use a set of for loops to add the files to the zip then activate an automatic download. The issue seems to be that the download starts before the files have been updated so they end up with no data.

for (var row = 0; row < 3; row++) { // Loop for rows
  for (var col = 0; col < 3; col++) { // Loop for cols
    if (cropSrc[row][col]) { // If data for cropped img exists
      var fileName = row + "" + col + ".png";
      zip.file(fileName, cropSrc[row][col].src, {
        base64: true
      }); // Add file to zip

      zip.file(fileName).async("uint8array").then(function(u8) { // Once file is complete... I think
        console.log("done");
      });

    }
  }
}

// Download zip file
zip.generateAsync({
  type: "base64"
}).then(function(base64) {
  window.location = "data:application/zip;base64," + base64;
});

I have to make sure I wait till all files have been updated before downloading, how would I do this using async functions?

Here is JSZip Async Documentation


Not sure if this will help but here is a more complete look at the full code, I'm using SAPUI5 and developing in SAP Web IDE:

var cropSrc = {
    0: {
        0:{src:"base64 data placeholder"},
        1:{src:"base64 data placeholder"},
        2:{src:"base64 data placeholder"}
    },
    1: {
        0:{src:"base64 data placeholder"},
        1:{src:"base64 data placeholder"},
        2:{src:"base64 data placeholder"}
    },
    2: {
        0:{src:"base64 data placeholder"},
        1:{src:"base64 data placeholder"},
        2:{src:"base64 data placeholder"}
    }
};

sap.ui.define([
    "sap/ui/core/mvc/Controller",
    "SAP_SARAH_ML_Project/libs/jszip", // Enables the jszip library (imports)
], function(Controller, jszipjs) { // Second part of importing
    "use strict";
    return Controller.extend("SAP_SARAH_ML_Project.controller.View1", {

        zipImg: function() {
            for (var row = 0; row < 3; row++) { // Loop for rows
                for (var col = 0; col < 3; col++) { // Loop for cols
                    if (cropSrc[row][col]) { // If data for cropped img exists
                        var fileName = row + "" + col + ".png";
                        zip.file(fileName, cropSrc[row][col].src, {
                            base64: true
                        }); // Add file to zip

                        zip.file(fileName).async("uint8array").then(function(u8) { // Once file is complete... I think
                            console.log("done");
                        });

                    }
                }
            }

            // Download zip file
            zip.generateAsync({
                type: "base64"
            }).then(function(base64) {
                window.location = "data:application/zip;base64," + base64;
            });
        }
    });
});

Solution

  • You can use Promise.all for this:

    let arr = [addFilePromise('file1'),addFilePromise('file2'),addFilePromise('file3')]
    Promise.all(arr).then(zip.generateAsync({type:"base64"}))
    

    EDIT:

    let fileAddActions = [];
    
    let addFilePromise = function(filename){
        zip.file(fileName, cropSrc[row][col].src, {
            base64: true
        }); 
    
        return zip.file(fileName).async("uint8array")
    }
    
    
    for (var row = 0; row < 3; row++) { // Loop for rows
        for (var col = 0; col < 3; col++) { // Loop for cols
            if (cropSrc[row][col]) { // If data for cropped img exists
            var fileName = row + "" + col + ".png";
            fileAddActions.push(addFilePromise(fileName))
    
            }
        }
    }
    
    
    Promise.all(fileAddActions).then(zip.generateAsync({type:"base64"})).then(base64 => {
        indow.location = "data:application/zip;base64," + base64;
    })
    

    something like this should work, also i recommend this article: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Promise