1

I want to download multiple files in a zip file. When the zip file downloads and I try opening it, I get the following error on macos 'unable to expand test into downloads (Error 1 - Operation not permitted)'.

Below is the code I use to generate zip files using jszip

var pages = $pagesComponentsDownloadSavetoFileinput[0].files;
var zip = new JSZip();

for(let i = 0; i < pages.length; i++) {
    let reader = new FileReader();
    reader.onload = function() {
        zip.file(pages[i].name, pages[i].content);
        console.log(zip) // returns proper file name and content objects
    }
    reader.readAsText(pages[i]);
}

window.setTimeout(function() { // setTimeout fixes the issue
    zip.generateAsync({type:"blob"}).then(function(blob) {
    console.log(blob); // returns Blob(22) {size: 22, type: "application/zip"} with no content
    saveAs(blob, "TESTING.zip");
  }, function (err) {
    console.log(err)
  });
}, 5000);

Edit: I'm using filesaver.js to save the zip file

EDIT2: The downloaded zip file is only 22 bytes large and doesn't contain the content added in the for loop.

EDIT 3: loggining 'blob' in generateAsync does not return the proper content. While logging zip in the for loop does.

EDIT 4: I added setTimeout of 5 seconds to zip.generateAsync and it fixed the issue. All files download after 5 seconds. It seems like zip.generateAsync triggers before the zip files are added in the for loop. Is there a solution that fixes this without using unreliable settimeout, perhaps a fallback once all files are added in the for loop?

  • While not exactly the same error, does `compression: "DEFLATE"` from https://stuk.github.io/jszip/documentation/faq.html#my-mac-generates-a-cpgz-file-when-i-try-to-extract-the-zip-file help ? – David Duponchel Jan 28 '18 at 13:15
  • Adding deflate did not solve the problem. –  Jan 28 '18 at 15:13
  • Your last edit ("The downloaded zip file is only 22 bytes large and doesn't contain the content added in the for loop.") is the main issue. Are you sure `pages` is non empty ? Could you check in `zip.files` after your loop ? – David Duponchel Jan 28 '18 at 15:39
  • "pages" contain HTML code string. I updated the code above to show what each section returns in console.log –  Jan 28 '18 at 15:50
  • Updated code to show that zip.file is within filereader function –  Jan 28 '18 at 16:02

1 Answers1

0

reader.readAsText is asynchronous: reader.onload will be called after everything else.

In the following code:

for (...) {
  reader.onload = function() {
    // 3
  };
  reader.readAsText(...); // 1
}
// 2

You will loop on every item of your array and register a callback (1), then continue outside of the loop (2). Finally, when the file reader ends its job, 3 will be executed. That's why adding a setTimeout "fix" the issue: after 5 seconds, the callbacks ended and filled in the zip object.

The fix: you could read asynchronously every blob and wait for all reads to end before calling zip.generateAsync. There is an easier way: JSZip v3 reads blobs so you can directly add it.

Edit:

Assuming pages is a FileList, you can do:

for(let i = 0; i < pages.length; i++) {
    // here, pages[i] is a File (https://developer.mozilla.org/en-US/docs/Web/API/File)
    zip.file(pages[i].name, pages[i]);
}
zip.generateAsync({type:"blob"}).then(function(blob) {...});

A File is a specialized Blob, so JSZip can read it to.

David Duponchel
  • 3,959
  • 3
  • 28
  • 36