Using archiver synchronously

2.5k views Asked by At

I want to zip a folder and then I have to delete it in a Gulp task. I'm trying to use archiver in a synchronous way.

I use the code like in the quick start guide in the npm page of archiver and it successfully create the zip file.

But if i try also to delete the zipped folder, then archiver has no enough time to create the zip.

const output = fs.createWriteStream(__dirname + '/example.zip');

//
// code as in https://www.npmjs.com/package/archiver
// ...

archive.finalize();

execSync(
  `rm -rf ${config.paths.dest}`,
);

Now I switched to zip-local package and it works in a synch way so I "solved" the problem, but now I'm forced to zip the entire folder, instead of selecting specific files and folders and the output zip is too much bigger.

I'm not an expert and I'm sure this is a problem caused only from my limits in understanding how archiver works.

Could someone help me?

Using archiver, I succeded in doing what I want, deleting the folder inside the "close" event:

output.on('close', function () {
  console.log(`Zip created`);
  execSync(
    `rm -rf ${config.paths.dest}`,
  );
});

But I don't like very much this solution.

I tried to use promises and watched a lot o videos but it is a concept that can not remain in my brain and I'm a little bit frustrated.

2

There are 2 answers

0
Qasim Hussain On

The best way to do is by using Promise (PromiseAll) here's the example

const promise = new Promise((resolve, reject) => {
  let dir = "./compress-files";
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir);
  }

  dir = `./compress-files/${sub}.zip`;
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir);
  }

  const password = Math.random().toString(36).slice(-8);
  var output = fs.createWriteStream(dir);
  var archive = archiver("zip-encryptable", {
    zlib: { level: 9 },
    forceLocalTime: true,
    password: password,
  });

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

  archive.on("error", function (err) {
    throw err;
    reject(false);
  });

  // pipe archive data to the output file
  archive.pipe(output);

  // append files
  archive.directory(`./excel-files/${sub}`, false);

  archive.finalize();

  console.log("zipped");
});

Promise.all([promise]).then(async (values) => {
  console.log("finally", values); /// perform action here
});
0
Tony Chung On

I don't know if this helps you, but I found archiver-promise from 2016/11/23 that wraps archiver in a promise.

I'm no expert in async/await or promises either, so my code might not be the greatest implementation. But it allows me to wrap the archiver to return a promise that I can watch before proceeding to the next step.

Posting here for others to validate. I'd really prefer an async/await implementation.

Example outcomes

Before I wrapped the routine in a promise, the report of total bytes archived displayed after the next action started.

Preparing archive: ../archives/example-archive.zip
Trigger next action
291302 total bytes
now we can work

After I wrapped the routine in a promise, the report of total bytes archived displays before triggering the next action.

Preparing archive: ../archives/example-archive.zip
291302 total bytes
now we can work
Trigger next action

Demo code with Promise

const fs = require('fs-extra')
const archiver = require('archiver-promise')

/**
 * Creates an archive of a folder of files. Could be modified to
 * archive specific files from the source folder.
 * @param {string} source_path - Full path to source folder
 * @param {string} archive_filename - Full path to archive name. Must end with `.zip`
 */
const archive_folder = (source_path, archive_filename) => {
  return new Promise((resolve, reject) => {
    console.log(`Preparing archive: ${archive_filename}`)

    const archive = archiver(archive_filename, {
      store: true
    })
  
    archive.directory(source_path)
  
    archive.finalize()
      .then(() => {
        console.log(`${archive.pointer()} total bytes`)
        // Resolve with a status object.
        resolve('now we can work')
      })
      .catch((err) => {
        reject(err)
      })
  })
}

// Calling routine
archive_folder('../example/source/folder', '../archives/example-archive.zip')
  .then((response) => {
    console.log(response)
  })
  .then(() => {
    console.log('Trigger next action')
    // continue next actions... 
  })
  .catch((err) => {
    console.error(err)
  })

Demo code without promise

/**
 * Creates an archive of a folder of files without promises.
 * @param {string} source_path - Full path to source folder
 * @param {string} archive_filename - Full path to archive name. Must end with `.zip`
 */
 const archive_folder_np = (source_path, archive_filename) => {
  console.log(`Preparing archive: ${archive_filename}`)

  const archive = archiver(archive_filename, {
    store: true
  })

  archive.directory(source_path)

  archive.finalize()
    .then(() => {
      console.log(`${archive.pointer()} total bytes`)
      console.log('now we can work')
    })
    .catch((err) => {
      console.error(err)
    })
}

// Calling routine
archive_folder_np('../example/source/folder', '../archives/example-archive.zip')
console.log('Trigger next action')