node.jsnpmunzipadm-zip

Unzip using adm-zip/unzipper/yauzl fails in node 10 silently


I've been trying to add a use case to my code in which I try to unzip a zip that is too large to fit in the disk space and I expect my code to throw ENOSPC. I've tried multiple libraries but none of them throw error rather fail silently without completing the zipping. I'd expect them to throw ENOSPC error but all the packages seem to log the first info statement which states that the unzipping has been started but nothing after that. Most of them create incomplete folders, whatever they could write before disk went out of space. Here is how my code looks like for each of the libraries.

My code using adm-zip:

exports.unzip = function(source, destination) {
    console.info("Started un-zipping from source: %s to destination: %s", source, destination);
    try {
        const zip = new AdmZip(source);
        zip.extractAllTo(destination, true);
        console.info("done unzipping");
    } catch (error) {
        console.error("Unzipping failed. Reason: %s", error)
        throw new Error(error)
    }
};

Code using yauzl:

exports.extractZip = function(source, destination) {
    return new Promise(function(resolve, reject) {
      console.log("Extracting zip: '" + source + "' to '" + destination + "'");

      yauzl.open(source, {
        lazyEntries: true
      }, function(err, zipfile) {
        if (err) throw err;
        zipfile.readEntry();
        zipfile.on("error", function (err) {
            console.error("Something went wrong while extracting!");
            reject(new Error(err));
        });
        zipfile.on("end", function () {
            console.log("Completed extracting zip!");
            resolve();
        });
        zipfile.on("entry", function(entry) {
          if (/\/$/.test(entry.fileName)) {
            // directory file names end with '/'
            mkdirp(destination + '/' + entry.fileName, function(err) {
              if (err) {
                  console.error("Something went wrong while extracting!");
                  throw err;
              }
              zipfile.readEntry();
            });
          } else {
            // file entry
            zipfile.openReadStream(entry, function(err, readStream) {
              if (err) {
                console.error("Something went wrong while extracting!");
                throw err;
              }
              // ensure parent directory exists
              mkdirp(destination + '/' + path.dirname(entry.fileName), function(err) {
                if (err) throw err;
                readStream.pipe(fs.createWriteStream(destination + '/' + entry.fileName));
                readStream.on("end", function() {
                  zipfile.readEntry();
                });
              });
            });
          }
        });
      });
    });
  }

Code using Unzipper:

exports.unzip2 = function(source, destination) {
    console.info("Started un-zipping from source: %s to destination: %s", source, destination);
    try {
        fs.createReadStream(source)
        .pipe(unzipper.Extract({ path: destination }))
        .on('error',function (err){
            console.error("something went wrong", err.code);
            throw err;
        });
    } catch (error) {
        console.error("Unzipping failed. Reason: %s", error)
        throw new Error(error)
    }
};

Code Using extract-zip:

exports.extractArchive = async function(source, destination) {
    try {
        extract(source, { dir: destination }, function (err) {
            if (err) {
                console.error("Something went wrong!", err.code);
                throw err;
            }
        });
        console.log('Extraction complete')
      } catch (err) {
        // handle any errors
      }
};

Is there something wrong with my code ? Is there any special event that I need to listen on ?


Solution

  • After some trail and error on both Yauzl and unzipper, unzipper seemed to work (throw ENOSPC when ran out of disk space during unzipping) with the following code.

    exports.unzip2 = function(source, destination) {
        return new Promise(function(resolve, reject) {
            console.info("Started un-zipping from source: %s to destination: %s", source, destination);
            try {
                var sourceStream = fs.createReadStream(source);
                sourceStream.on('error',function (err){
                    console.error("something went wrong", err.code);
                    reject(new Error(err));
                });
                var destinationStream = unzipper.Extract({ path: destination });
                destinationStream.on('error',function (err){
                    console.error("something went wrong", err.code);
                    reject(new Error(err));
                });
                destinationStream.on('close',function (){
                    console.log("Completed extract!");
                    resolve();
                });
                sourceStream.pipe(destinationStream).on('error',function (err){
                    console.error("something went wrong", err.code);
                    reject(new Error(err));
                });;
            } catch (error) {
                console.error("something went wrong", err.code);
                reject(new Error(err));
            }
        });
    };