{"id":16259,"library":"unzip-stream","title":"Unzip Stream","description":"unzip-stream is a Node.js library for processing zip files using a streaming API. It provides mechanisms to parse zip file contents entry by entry or extract an entire archive to a directory. The current stable version is 0.3.4, with a release cadence that appears to be maintenance-focused rather than frequent feature additions. Key differentiators include its streaming engine, which aims to handle zip files that might cause issues with older libraries like `unzip` or `unzipper`, and its reliance solely on Node.js's built-in zlib for inflation, avoiding compiled dependencies. It supports Zip64 for archives with files larger than 4GB. However, the library acknowledges that the zip file format is not inherently optimized for streaming, suggesting alternatives like `yauzl` or `decompress-zip` for scenarios where the complete zip file is available and random access is preferred. It lacks support for encrypted (password-protected) zips and symlinks.","status":"maintenance","version":"0.3.4","language":"javascript","source_language":"en","source_url":"https://github.com/mhr3/unzip-stream","tags":["javascript","zip","unzip","zlib","uncompress","archive","stream","extract"],"install":[{"cmd":"npm install unzip-stream","lang":"bash","label":"npm"},{"cmd":"yarn add unzip-stream","lang":"bash","label":"yarn"},{"cmd":"pnpm add unzip-stream","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"For ESM, `import * as unzip from 'unzip-stream'` is the robust way to access named exports from a CommonJS module. For CJS, `require` is correct.","wrong":"const unzip = require('unzip-stream');","symbol":"unzip","correct":"import * as unzip from 'unzip-stream';"},{"note":"Access `Parse` as a property of the imported `unzip` object.","symbol":"unzip.Parse","correct":"import * as unzip from 'unzip-stream';\nunzip.Parse();"},{"note":"Access `Extract` as a property of the imported `unzip` object.","symbol":"unzip.Extract","correct":"import * as unzip from 'unzip-stream';\nunzip.Extract();"}],"quickstart":{"code":"import fs from 'fs';\nimport path from 'path';\nimport * as unzip from 'unzip-stream';\n\nconst archivePath = path.resolve('./example.zip');\nconst outputPath = path.resolve('./extracted_files');\n\n// Create a dummy zip file for demonstration if it doesn't exist\nif (!fs.existsSync(archivePath)) {\n  console.warn('Creating a dummy example.zip for quickstart. In a real scenario, use a valid zip file.');\n  const JSZip = await import('jszip'); // Dynamic import for JSZip\n  const zip = new JSZip.default();\n  zip.file('hello.txt', 'Hello, Unzip Stream!');\n  zip.file('folder/world.txt', 'World!');\n  const content = await zip.generateAsync({ type: 'nodebuffer' });\n  fs.writeFileSync(archivePath, content);\n}\n\n// Ensure output directory exists\nif (!fs.existsSync(outputPath)) {\n  fs.mkdirSync(outputPath, { recursive: true });\n}\n\nfs.createReadStream(archivePath)\n  .pipe(unzip.Parse())\n  .on('entry', function (entry) {\n    const filePath = entry.path;\n    const type = entry.type; // 'Directory' or 'File'\n    const size = entry.size; // might be undefined in some archives\n\n    console.log(`Processing entry: ${filePath} (Type: ${type}, Size: ${size || 'unknown'})`);\n\n    if (type === 'File' && filePath === 'hello.txt') {\n      const writeStream = fs.createWriteStream(path.join(outputPath, path.basename(filePath)));\n      entry.pipe(writeStream);\n      writeStream.on('finish', () => console.log(`Extracted ${filePath}`));\n    } else {\n      // Important: Call autodrain() for entries you don't consume to prevent stream from getting stuck.\n      entry.autodrain();\n      if (type === 'File') console.log(`Skipping extraction for ${filePath}, autodraining.`);\n    }\n  })\n  .on('close', () => {\n    console.log('Finished parsing the archive. Check output in:', outputPath);\n  })\n  .on('error', (err) => {\n    console.error('An error occurred during parsing:', err);\n  });\n","lang":"typescript","description":"This example demonstrates how to parse a zip file, iterate through its entries, and selectively extract a specific file. It highlights the crucial `autodrain()` method for unconsumed entries and proper event handling."},"warnings":[{"fix":"Ensure every `entry` stream is either piped to a consumer (e.g., `fs.createWriteStream`) or explicitly calls `entry.autodrain()`.","message":"When parsing a zip file using `unzip.Parse()`, you must explicitly consume the data from each entry stream (e.g., by piping it to a write stream) or call `entry.autodrain()` if you do not intend to process its contents. Failure to do so will cause the main archive stream to become stuck and prevent further entries from being processed.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Listen for the `'close'` event on the stream returned by `unzip.Extract()` instead of `'finish'` for reliable completion detection.","message":"When using `unzip.Extract()`, rely on the 'close' event to determine when the entire archive has been fully extracted and all files have been written to disk. The 'finish' event can be emitted prematurely, before all asynchronous file writing operations have completed, leading to incomplete extractions.","severity":"gotcha","affected_versions":">=0.2.0"},{"fix":"Upgrade to `unzip-stream@0.3.0` or later to ensure full compatibility with Zip64 archives and large file sizes.","message":"Older versions of `unzip-stream` (prior to 0.3.0) had limited or no support for Zip64, meaning they could fail to process archives or individual files larger than 4GB. Archives containing such large files would either error out or produce corrupted data.","severity":"breaking","affected_versions":"<0.3.0"},{"fix":"Evaluate if a streaming approach is strictly necessary. For complete zip files, consider non-streaming libraries. For streaming, be prepared for potential edge case issues.","message":"The zip file format is not ideally suited for streaming, as it traditionally places metadata at the end of the file. While `unzip-stream` attempts to mitigate this with a new streaming engine, edge cases or corrupted archives might still behave unpredictably. For scenarios with complete zip files, alternative libraries like `yauzl` or `decompress-zip` (which read from the end of the file) might offer greater robustness.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Ensure zip archives are not encrypted if using `unzip-stream`. For symlink support or encryption, choose an alternative library that explicitly offers these features.","message":"`unzip-stream` does not support encrypted (password-protected) zip files or UNIX symlinks within archives. Attempts to process such archives will likely result in errors or incorrect extraction.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Pass a `decodeString` option to `unzip.Parse()` or `unzip.Extract()` that utilizes a library like `iconv-lite` for correct character set decoding, e.g., `{ decodeString: (buffer) => iconvLite.decode(buffer, 'iso-8859-2') }`.","message":"When dealing with non-UTF8 filenames within zip archives, the default decoding might lead to garbled characters. The `Parse` and `Extract` methods allow providing a custom `decodeString` function to handle specific encodings.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"For every `entry` object, either pipe it to a destination (e.g., `entry.pipe(fs.createWriteStream('path'))`) or call `entry.autodrain()` to dispose of its contents.","cause":"An entry stream was received from `unzip.Parse()` but its data was neither consumed nor explicitly discarded.","error":"Stream is stuck, no more entries are processed."},{"fix":"Change event listener from `'finish'` to `'close'` when using `unzip.Extract()`: `stream.on('close', () => { /* extraction truly done */ });`","cause":"The `unzip.Extract()` stream emitted the 'finish' event before all files were fully written to disk.","error":"Files are missing or incomplete after extraction, but the stream's 'finish' event fired."},{"fix":"Provide a custom `decodeString` function using a character encoding library (like `iconv-lite`) to `unzip.Parse()` or `unzip.Extract()`, e.g., `unzip.Parse({ decodeString: (buffer) => iconvLite.decode(buffer, 'cp866') })`.","cause":"The zip archive contains filenames encoded in a character set other than UTF-8, and `unzip-stream` is using its default or incorrect fallback decoding.","error":"Filenames within the extracted archive appear as garbled characters (e.g., '����.txt')."},{"fix":"Upgrade your `unzip-stream` package to version `0.3.0` or newer: `npm install unzip-stream@latest`.","cause":"You are using an older version of `unzip-stream` (prior to 0.3.0) which lacks full support for Zip64, trying to process archives or files larger than 4GB.","error":"Error: Zip64 required for files over 4GB. (Or similar errors with large files)"}],"ecosystem":"npm"}