user21039427
user21039427

Reputation: 11

Nodejs Archiver ArchiverError: archive already finalizing

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.

Upvotes: 0

Views: 1128

Answers (2)

Koray
Koray

Reputation: 154

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();
  }
};

Upvotes: 1

user21039427
user21039427

Reputation: 11

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);
};

Upvotes: 0

Related Questions