Nodejs Archiver ArchiverError: archive already finalizing

771 Views Asked by At

I want to zip folders excluding certain files/folders so I used the solution in this answer. The solution works for a single folder but throws error when there are multiple folders to be zipped.

I need to make zips from each sub-folders of the source directory

Rootfolder:
 |-folder1
 |-folder2
 |-folder3

should result in:

build:
 |-{timeFormat}_folder1.zip
 |-{timeFormat}_folder2.zip
 |-{timeFormat}_folder3.zip

I am getting following error:

".../node_modules/archiver/lib/core.js:778
    var finalizingError = new ArchiverError('FINALIZING');
                          ^
ArchiverError: archive already finalizing
...
 at processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'FINALIZING',
  data: undefined
}"

My code is as follows:

const zipFolders = async (
  rootPath: string,
  dirPaths: string[]
): Promise<void> => {
  for await (const folder of dirPaths) {
    await makeZip(folder, rootPath);
  }
};

.........

const fs = require("fs");
const archiver = require("archiver");
const archive = archiver("zip");
import { format } from "date-fns";

const exclude =["assets/**","**/*.json"]

export const makeZip = (srcFolder: string, rootPath: string): Promise<void> =>
  new Promise((resolve) => {
    const timestamp = format(new Date(), "MM-DD-YYYY_HH-mm-ssZZ");
    const dirParts = srcFolder.split("/");
    const dirName = dirParts[dirParts.length - 1];
    const zipName = `${timestamp}_${dirName}`;
    const zipPath = `${rootPath}/build/${zipName}.zip`;


    const output = fs.createWriteStream(zipPath);
    output.on("close", function () {
      console.log("done writing: " + archive.pointer() + " total bytes");
      resolve();
    });
    archive.pipe(output);

    archive.on("error", function (err: Error) {
      throw err;
    });

    archive.glob("**/*", {
      cwd: srcFolder,
      ignore: exclude,
    });

    archive.finalize();
  });

I need some help regarding what needs to be updated or if there is any other way of achieving the same thing. Thank you.

2

There are 2 best solutions below

0
On BEST ANSWER

Posting the solution I ended up using.

const exclude = ["assets/**", "**/*.json"];

const makeZip = (srcFolder: string, rootPath: string): Promise<void> =>
  new Promise((resolve, reject) => {
    const timestamp = format(new Date(), "MM-DD-YYYY_HH-mm-ssZZ");
    const dirParts = srcFolder.split("/");
    const dirName = dirParts[dirParts.length - 1];
    const zipName = `${timestamp}_${dirName}`;
    const zipPath = `${rootPath}/build/${zipName}.zip`;

    const archive = archiver("zip");
    const output = fs.createWriteStream(zipPath);

    output.on("close", function () {
      console.log("done writing: " + archive.pointer() + " total bytes");
      resolve();
    });

    archive.on("error", function (err: Error) {
      reject(err);
    });

    archive.glob("**/*", {
      cwd: srcFolder,
      ignore: exclude,
    });

    archive.pipe(output);
    archive.finalize();
  });

......

const zipFolders = async (rootPath: string, dirPaths: string[]): Promise<void> => {
  const promises = dirPaths.map((folder) => makeZip(folder, rootPath));
  await Promise.all(promises);
};
2
On

You need to move the finalize() method call outside of the makeZip function and call it only once after all the folders have been added to the archive. Here's an updated version of your zipFolders function that should work:

const zipFolders = async (
  rootPath: string,
  dirPaths: string[]
): Promise<void> => {
  const archive = archiver('zip');
  const output = fs.createWriteStream(`${rootPath}/build/archive.zip`);
  output.on("close", function () {
    console.log("done writing: " + archive.pointer() + " total bytes");
  });
  archive.pipe(output);

  for await (const folder of dirPaths) {
    const timestamp = format(new Date(), "MM-DD-YYYY_HH-mm-ssZZ");
    const dirParts = folder.split("/");
    const dirName = dirParts[dirParts.length - 1];
    const zipName = `${timestamp}_${dirName}`;
    const zipPath = `${rootPath}/build/${zipName}.zip`;

    await makeZip(archive, folder, zipPath);
  }

  archive.finalize();
};

export const makeZip = (
  archive: archiver.Archiver,
  srcFolder: string,
  zipPath: string
): Promise<void> =>
  new Promise((resolve) => {
    const exclude =["assets/**","**/*.json"]

    const output = fs.createWriteStream(zipPath);
    output.on("close", function () {
      console.log("done writing: " + archive.pointer() + " total bytes");
      resolve();
    });
    archive.pipe(output);

    archive.on("error", function (err: Error) {
      throw err;
    });

    archive.glob("**/*", {
      cwd: srcFolder,
      ignore: exclude,
    });
  });

Answer to other question:

If you want to create separate zip files for each subfolder, you can modify the zipFolders function to create a new archive instance for each subfolder, like this:

const zipFolders = async (rootPath: string, dirPaths: string[]): Promise<void> => {
  for await (const folder of dirPaths) {
    const timestamp = format(new Date(), "MM-DD-YYYY_HH-mm-ssZZ");
    const dirParts = folder.split("/");
    const dirName = dirParts[dirParts.length - 1];
    const zipName = `${timestamp}_${dirName}`;
    const zipPath = `${rootPath}/build/${zipName}.zip`;

    const archive = archiver("zip");
    const output = fs.createWriteStream(zipPath);

    output.on("close", function () {
      console.log("done writing: " + archive.pointer() + " total bytes");
    });

    archive.on("error", function (err: Error) {
      throw err;
    });

    archive.glob("**/*", {
      cwd: folder,
      ignore: exclude,
    });

    archive.pipe(output);
    archive.finalize();
  }
};