CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-unzip

Streaming cross-platform unzip library for Node.js compatible with fstream and fs.ReadStream

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

index.mddocs/

Unzip

Unzip is a streaming cross-platform unzip library for Node.js that provides simple APIs for parsing and extracting zip files. It offers compatibility with fstream and fs.ReadStream interfaces without compiled dependencies, using Node.js's built-in zlib support for inflation.

Package Information

  • Package Name: unzip
  • Package Type: npm
  • Language: JavaScript (Node.js)
  • Installation: npm install unzip

Core Imports

const unzip = require('unzip');
// Or destructured
const { Parse, Extract } = require('unzip');

ES6 modules (if supported by your Node.js version):

import unzip from 'unzip';
import { Parse, Extract } from 'unzip';

Basic Usage

Extract to Directory

const fs = require('fs');
const unzip = require('unzip');

// Extract entire zip archive to a directory
fs.createReadStream('path/to/archive.zip')
  .pipe(unzip.Extract({ path: 'output/path' }));

Parse and Process Entries

const fs = require('fs');
const unzip = require('unzip');

// Parse zip file and process individual entries
fs.createReadStream('path/to/archive.zip')
  .pipe(unzip.Parse())
  .on('entry', function (entry) {
    const fileName = entry.path;
    const type = entry.type; // 'Directory' or 'File'
    const size = entry.size;
    
    if (fileName === "target-file.txt") {
      entry.pipe(fs.createWriteStream('output.txt'));
    } else {
      entry.autodrain(); // Important: drain unused entries
    }
  });

Architecture

Unzip is built around three core components:

  • Parse: Transform stream that parses zip archive structure and emits entry events
  • Extract: Writable stream that extracts entire archives to directories using fstream
  • Entry: PassThrough stream representing individual zip entries (files/directories)

The library uses a streaming approach for memory efficiency, processing zip files without loading entire contents into memory.

Capabilities

Zip Archive Parsing

Parse zip files and access individual entries with streaming support.

/**
 * Creates a streaming zip parser (can be called with or without 'new')
 * @param {Object} opts - Configuration options
 * @param {boolean} opts.verbose - Enable verbose logging (default: false)
 * @returns {Parse} Transform stream for parsing zip archives
 */
function Parse(opts);

/**
 * Factory method equivalent to new Parse(opts) - preferred way to create instances
 * @param {Object} opts - Configuration options
 * @returns {Parse} Transform stream instance
 */
Parse.create = function(opts);

/**
 * Enhanced pipe method with fstream compatibility
 * Automatically calls dest.add(entry) for each entry if destination has an add method
 * @param {Object} dest - Destination stream or fstream Writer
 * @param {Object} opts - Standard pipe options
 * @returns {Object} The destination stream
 */
Parse.prototype.pipe = function(dest, opts);

/**
 * Enhanced event listener registration that tracks 'entry' listeners
 * When entry listeners are added, the parser will emit entry events and provide entry streams
 * @param {string} type - Event type ('entry', 'error', 'close', etc.)
 * @param {Function} listener - Event handler function
 * @returns {Parse} This Parse instance for chaining
 */
Parse.prototype.addListener = function(type, listener);
Parse.prototype.on = function(type, listener); // Alias for addListener

The Parse stream emits the following events:

  • 'entry' - Emitted for each zip entry (file/directory)
  • 'error' - Emitted on parsing errors
  • 'close' - Emitted when parsing completes
  • 'end' - Emitted when stream ends
  • 'finish' - Emitted when stream finishes

Directory Extraction

Extract entire zip archives to filesystem directories.

/**
 * Creates a writable stream for extracting zip archives to directories (can be called with or without 'new')
 * @param {Object} opts - Configuration options
 * @param {string} opts.path - Target extraction directory (required)
 * @param {boolean} opts.verbose - Enable verbose logging (default: false)
 * @returns {Extract} Writable stream for zip extraction
 */
function Extract(opts);

The Extract stream emits the following events:

  • 'error' - Emitted on extraction errors
  • 'close' - Emitted when extraction completes
  • 'pipe' - Emitted when piped to
  • 'finish' - Emitted when stream finishes

Zip Entry Processing

Individual zip entries are represented as Entry streams.

/**
 * PassThrough stream representing a zip entry
 * Note: Entry instances are created internally by Parse, not directly by users
 * @constructor
 */
function Entry();

/**
 * Automatically drain entry content to prevent memory issues
 * Call this for entries you don't intend to process
 */
Entry.prototype.autodrain = function();

Entry objects have the following properties (set by Parse during processing):

  • path (string) - Entry file path within the zip archive
  • type (string) - Entry type: 'File' or 'Directory' (determined by compressed size and path ending)
  • size (number) - Entry uncompressed size (available after processing, may be undefined during parsing)
  • props (object) - Additional entry properties including a copy of the path

Types

/**
 * Parse stream class (extends Node.js Transform stream)
 */
class Parse {
  constructor(opts);
  
  // Enhanced pipe method with automatic fstream compatibility
  // Calls dest.add(entry) for each entry if destination has add method
  pipe(dest, opts);
  
  // Enhanced event listener methods that track 'entry' listeners
  // When entry listeners are registered, parser provides entry streams
  addListener(type, listener);
  on(type, listener); // Alias for addListener
  
  // Standard Transform stream methods available
  write(chunk, encoding, callback);
  end();
  // ... other Transform stream methods
}

/**
 * Extract stream class (extends Node.js Writable stream)
 */
class Extract {
  constructor(opts);
  
  // Standard Writable stream methods available
  write(chunk, encoding, callback);
  end();
  // ... other Writable stream methods
}

/**
 * Entry stream class (extends Node.js PassThrough stream)
 */
class Entry {
  constructor();
  autodrain();
  
  // Properties set by Parse during zip processing
  path: string;
  type: 'File' | 'Directory';
  size: number;
  props: object;
  
  // Standard PassThrough stream methods available
  pipe(destination, options);
  read(size);
  write(chunk, encoding, callback);
  // ... other stream methods
}

/**
 * Configuration options for Parse
 */
interface ParseOptions {
  verbose?: boolean;
}

/**
 * Configuration options for Extract
 */
interface ExtractOptions {
  path: string;
  verbose?: boolean;
}

Error Handling

The library emits errors for various conditions:

  • Invalid zip signatures: Throws errors with signature details (e.g., "invalid signature: 0x12345678")
  • Stream errors: Propagated through 'error' events on Parse and Extract streams
  • Compression errors: zlib inflation errors are propagated through the error event chain
const parser = unzip.Parse();
parser.on('error', function(err) {
  console.error('Parse error:', err.message);
});

const extractor = unzip.Extract({ path: './output' });
extractor.on('error', function(err) {
  console.error('Extract error:', err.message);
});

Advanced Usage

Pipe to fstream Writer

const fs = require('fs');
const fstream = require('fstream');
const unzip = require('unzip');

const readStream = fs.createReadStream('archive.zip');
const writeStream = fstream.Writer('output/path');

readStream
  .pipe(unzip.Parse())
  .pipe(writeStream);

Selective File Extraction

fs.createReadStream('archive.zip')
  .pipe(unzip.Parse())
  .on('entry', function (entry) {
    if (entry.path.endsWith('.txt')) {
      // Extract only text files
      entry.pipe(fs.createWriteStream(`output/${entry.path}`));
    } else {
      // Skip other files
      entry.autodrain();
    }
  });

Memory Management

Important: Always call entry.autodrain() for entries you don't process to prevent memory leaks:

fs.createReadStream('large-archive.zip')
  .pipe(unzip.Parse())
  .on('entry', function (entry) {
    if (entry.type === 'Directory') {
      // Skip directories
      entry.autodrain();
    } else if (entry.size > 1000000) {
      // Skip large files
      entry.autodrain();
    } else {
      // Process small files
      entry.pipe(fs.createWriteStream(entry.path));
    }
  });

Enhanced Parse Class Features

Automatic fstream Compatibility

The Parse class automatically detects when piped to fstream destinations and calls dest.add(entry) for each zip entry:

// Enhanced pipe method detects fstream Writers and calls add() automatically
fs.createReadStream("archive.zip")
  .pipe(unzip.Parse())
  .pipe(fstream.Writer("output/path")); // Automatically calls writeStream.add(entry)

Entry Listener Optimization

The Parse class optimizes behavior based on whether entry listeners are registered:

// Without entry listeners - entries are not processed into streams (memory efficient)
fs.createReadStream("archive.zip")
  .pipe(unzip.Parse())
  .pipe(fstream.Writer("output"));

// With entry listeners - entries become readable streams
fs.createReadStream("archive.zip")
  .pipe(unzip.Parse())
  .on("entry", function(entry) {
    // Entry is now a readable stream with available properties
    console.log("Processing:", entry.path, "Type:", entry.type);
    if (entry.size) console.log("Size:", entry.size);
    entry.autodrain(); // Important: drain if not processing
  });

docs

index.md

tile.json