user1873073 user1873073 - 6 months ago 167
Node.js Question

How to zip a directory, recursively, without getting malformed files when you try to unzip in node.js

So far I have tried ADM-ZIP and easy-zip. Both of them create a zip that is mostly successful but has some malformed files:

enter image description here

This happens with a variety of file types (including just regular pictures and html pages). I suspect that the file type isn't even an issue. Either way, I can't use it if it doesn't work perfectly.

Some people have suggested node-archive but there are no instructions for how to zip a file, let alone recursively zip a directory while keeping its file structure.

update

as requested here is the code i am using (adm-zip)

var Zip = require("adm-zip");
var zip = new Zip();
zip.addLocalFolder("C:\\test");
zip.writeZip("C:\\test\\color.zip");

Answer

As you had mentioned, I would use node-archiver. You can get nested folders from node-archiver by:

var archiver = require('archiver');

var archive = archiver('zip')

// create your file writeStream - let's call it writeStream

archive.pipe(writeStream);

// get all the subfiles/folders for the given folder - this can be done with readdir
// take a look at http://nodejs.org/api/fs.html#fs_fs_readdir_path_callback
// and http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search
// appending to the archiver with a name that has slashes in it will treat the payload as a subfolder/file

// iterate through the files from the previous walk
ITERATE_THROUGH_RESULTS_FROM_WALK
    // look into http://nodejs.org/api/fs.html#fs_fs_readfile_filename_options_callback
    GET_READ_STREAM_ON_FILE_FROM_FS
        archive.append((new Buffer(data), 'utf-8'), {name: NAME_WITH_SLASHES_FOR_PATH});

archive.finalize(function (err) {
    // handle the error if there is one
});

Hopefully this is a pretty good nudge in the right direction (basically all of the steps are there for you). If it isn't clear yet:

  1. Create your archive with the 'zip' option.
  2. Pipe to a writestream that you created for where you want to save your zip file.
  3. Walk through the folder recursively, saving paths in a collection (array) as you go along.
  4. Iterate through this collection of paths, opening each file.
  5. When you have the data of each file, create a Buffer from it and pass that as well as the path into archive.append.
  6. Call archive.finalize.

For sake of readability, and because I believe I have given you pretty much all the steps you would need, I did not include all of the code (most notably the walk - which is laid out in the link anyhow).