angulartypescriptjszipfilesaver.js

Angular 11 - Compress using JSZip library


I have to compress several txt files in a zip that come to me from the response of a service in base64 format.

This is the code to download the zip with its compressed txt files under the "txt" folder:

let zip = new JSZip();
zip.file("readme.txt", "Description content");
let txtFile = zip.folder("txt");
this.selectedItems?.forEach((item) => {
    this.downloadService
    .downloadFile(item.name)
    .subscribe((response) => {
      let base64 = response.output.split(",");
      txtFile.file(item.name, base64[1], {base64: true});
    });
});
zip.generateAsync({type:"blob"})
.then(function(content) {
  // see FileSaver.js
  FileSaver.saveAs(content, "fileTxt.zip");
});

"selectedItems": is an array of objects with several files, which if they exist, will be compressed inside the "txt" folder of the zip file and "item.name", is the property of array of objects with the name of the file..

I have two problems:

1. Dynamic name of the zip file

I need to put a dynamic name to the zip file. For this I created a class attribute where I store the name "fileZipName" (The value of fileZipName, Iassign it in the onInit event of the component).

  zip.generateAsync({type:"blob"})
  .then(function(content) {
      // see FileSaver.js
      FileSaver.saveAs(content, this.fileZipName);
  });

When using the variable "fileZipName" inside the "then", it shows me the following error in browser console:

core.js:6210 ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of undefined (reading 'fileZipName')
TypeError: Cannot read properties of undefined (reading 'fileZipName')

2. Add files to zip

If I give it a fixed name, for example "filesTxt.zip" it works fine, it generates the zip file correctly, it includes the "readme.txt" file in the zip, it adds the "txt" folder in the zip, but inside the "txt" folder does not show the file that I need to compress, the folder "txt" is empty.

"base64[1]", contains the base64 code of the txt file: "VGVzdCBJbmZyYTEw", in fact if I go to an online website to decode it, it returns the txt file correctly.

I don't get any error.

Could you help me? Thank you,


Solution

  • For the first problem, I recommend reading adding JavaScript context in order to understand the problem you are having, but in order to fix your issue, just replace the traditional function expression to an arrow function expression, so:

    From

    zip.generateAsync({type:"blob"})
      .then(function(content) {
        // see FileSaver.js
        FileSaver.saveAs(content, this.fileZipName);
    });
    

    To

    zip.generateAsync({type:"blob"})
      .then((content) => {
        // see FileSaver.js
        FileSaver.saveAs(content, this.fileZipName);
    });
    

    In the first expression, the this keyword is not pointing to the class instance (hence the undefined value), while in the second expression, it is.

    For the second problem, I have never used that JSZip but from the documentation, what I understood is that the folder function will just create a folder on the output zip. In order to have the subfiles with it, you need to add them yourself.

    let zip = new JSZip();
    zip.file("readme.txt", "Description content");
    
    // Option 1 (manually add all the files)
    let txtFile = zip.folder("txt").file('file1.txt').file('file2.txt');
    // Option 2 (add all subfiles indiscriminately) - Did not test this
    let txtFile = zip.folder("txt").file(/.*/);