Cross-platform streaming API for ZIP file extraction and manipulation in Node.js environments
—
Promise-based API for opening ZIP files from multiple sources (filesystem, URLs, S3, buffers) and accessing their contents with random access patterns. This high-level interface provides convenient methods for reading ZIP files without streaming, ideal for applications that need to inspect or selectively extract files.
Opens ZIP files from local filesystem paths.
/**
* Opens a ZIP file from the filesystem
* @param path - File system path to the ZIP file
* @param options - Optional configuration object
* @returns Promise resolving to Directory object
*/
static async file(path: string, options?: OpenOptions): Promise<Directory>;Usage Examples:
const unzipper = require("unzipper");
// Basic file opening
const directory = await unzipper.Open.file("archive.zip");
console.log(`Archive contains ${directory.files.length} files`);
// List all files
directory.files.forEach(file => {
console.log(`${file.type}: ${file.path}`);
});
// Find specific file
const readme = directory.files.find(f => f.path === "README.md");
if (readme) {
const content = await readme.buffer();
console.log(content.toString());
}
// Extract specific files
const configFile = directory.files.find(f => f.path.endsWith("config.json"));
if (configFile) {
const configStream = configFile.stream();
configStream.pipe(fs.createWriteStream("config.json"));
}Opens ZIP files from HTTP/HTTPS URLs using a provided request library.
/**
* Opens a ZIP file from a URL
* @param requestLibrary - HTTP request library (e.g., 'request' module)
* @param url - URL to the ZIP file
* @param options - Optional configuration object
* @returns Promise resolving to Directory object
*/
static async url(requestLibrary: any, url: string, options?: OpenOptions): Promise<Directory>;Usage Examples:
const request = require("request");
// Download and open ZIP file
const directory = await unzipper.Open.url(request, "https://example.com/archive.zip");
// Process files from remote ZIP
for (const file of directory.files) {
if (file.type === "File" && file.path.endsWith(".txt")) {
const content = await file.buffer();
console.log(`${file.path}: ${content.length} bytes`);
}
}
// Stream large file from remote ZIP
const largeFile = directory.files.find(f => f.path === "data/large-dataset.csv");
if (largeFile) {
largeFile.stream().pipe(fs.createWriteStream("dataset.csv"));
}Opens ZIP files stored in Amazon S3 buckets.
/**
* Opens a ZIP file from Amazon S3
* @param awsSdk - AWS SDK instance
* @param params - S3 parameters (Bucket, Key, etc.)
* @param options - Optional configuration object
* @returns Promise resolving to Directory object
*/
static async s3(awsSdk: any, params: S3Parameters, options?: OpenOptions): Promise<Directory>;
interface S3Parameters {
/** S3 bucket name */
Bucket: string;
/** S3 object key */
Key: string;
/** Optional version ID for versioned objects */
VersionId?: string;
/** Additional S3 parameters as per AWS SDK */
[key: string]: any;
}Usage Examples:
const AWS = require("aws-sdk");
const s3 = new AWS.S3();
// Open ZIP file from S3
const directory = await unzipper.Open.s3(s3, {
Bucket: "my-bucket",
Key: "archives/data.zip"
});
// Process S3-hosted ZIP contents
const manifest = directory.files.find(f => f.path === "manifest.json");
if (manifest) {
const manifestData = JSON.parse((await manifest.buffer()).toString());
console.log("Archive manifest:", manifestData);
}
// Extract multiple files from S3 ZIP
const imageFiles = directory.files.filter(f =>
f.type === "File" && /\.(jpg|png|gif)$/i.test(f.path)
);
for (const imageFile of imageFiles) {
const fileName = path.basename(imageFile.path);
imageFile.stream().pipe(fs.createWriteStream(`images/${fileName}`));
}Opens ZIP files from in-memory buffers.
/**
* Opens a ZIP file from a memory buffer
* @param buffer - Buffer containing ZIP file data
* @param options - Optional configuration object
* @returns Promise resolving to Directory object
*/
static async buffer(buffer: Buffer, options?: OpenOptions): Promise<Directory>;Usage Examples:
// Open ZIP from buffer (e.g., from database or API)
const zipBuffer = await loadZipFromDatabase();
const directory = await unzipper.Open.buffer(zipBuffer);
// Process in-memory ZIP
directory.files.forEach(async (file) => {
if (file.path.startsWith("config/")) {
const content = await file.buffer();
await processConfigFile(file.path, content);
}
});
// Convert buffer ZIP to file system
const tempFiles = directory.files.filter(f => f.type === "File");
for (const file of tempFiles) {
const outputPath = `temp/${file.path}`;
const outputDir = path.dirname(outputPath);
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const content = await file.buffer();
fs.writeFileSync(outputPath, content);
}Opens ZIP files from custom source implementations.
/**
* Opens a ZIP file from a custom source
* @param source - Custom source implementation
* @param options - Optional configuration object
* @returns Promise resolving to Directory object
*/
static async custom(source: any, options?: OpenOptions): Promise<Directory>;The Directory object returned by Open methods provides access to the ZIP file contents.
interface Directory {
/** Array of files and directories in the ZIP archive */
files: File[];
/** Extract all files to filesystem (if supported) */
extract(): Promise<void>;
}
interface File {
/** Path of the file/directory within the archive */
path: string;
/** Type of entry: 'File' or 'Directory' */
type: 'File' | 'Directory';
/** Creates a readable stream for the file content */
stream(): ReadableStream;
/** Returns the file content as a Buffer */
buffer(): Promise<Buffer>;
}Usage Examples:
const directory = await unzipper.Open.file("project.zip");
// Explore directory structure
console.log("Files in archive:");
directory.files
.filter(f => f.type === "File")
.forEach(f => console.log(` ${f.path}`));
console.log("Directories in archive:");
directory.files
.filter(f => f.type === "Directory")
.forEach(f => console.log(` ${f.path}/`));
// Extract entire archive (if supported)
await directory.extract();
// Work with specific files
const packageJson = directory.files.find(f => f.path === "package.json");
if (packageJson) {
// As buffer
const packageData = JSON.parse((await packageJson.buffer()).toString());
console.log(`Package: ${packageData.name}@${packageData.version}`);
// As stream
packageJson.stream().pipe(fs.createWriteStream("package.json"));
}const inspectArchive = async (zipPath) => {
const directory = await unzipper.Open.file(zipPath);
const stats = {
totalFiles: 0,
totalDirectories: 0,
totalSize: 0,
fileTypes: new Map(),
largestFile: null,
maxSize: 0
};
for (const file of directory.files) {
if (file.type === "File") {
stats.totalFiles++;
// Get file size by reading buffer
const buffer = await file.buffer();
const size = buffer.length;
stats.totalSize += size;
if (size > stats.maxSize) {
stats.maxSize = size;
stats.largestFile = file.path;
}
// Track file extensions
const ext = path.extname(file.path).toLowerCase();
stats.fileTypes.set(ext, (stats.fileTypes.get(ext) || 0) + 1);
} else {
stats.totalDirectories++;
}
}
return stats;
};
const stats = await inspectArchive("project.zip");
console.log(`Archive contains ${stats.totalFiles} files and ${stats.totalDirectories} directories`);
console.log(`Total size: ${stats.totalSize} bytes`);
console.log(`Largest file: ${stats.largestFile} (${stats.maxSize} bytes)`);const processArchiveByType = async (zipPath) => {
const directory = await unzipper.Open.file(zipPath);
const processors = {
'.json': async (file) => {
const content = JSON.parse((await file.buffer()).toString());
console.log(`JSON file ${file.path}:`, Object.keys(content));
},
'.txt': async (file) => {
const content = (await file.buffer()).toString();
console.log(`Text file ${file.path}: ${content.split('\n').length} lines`);
},
'.md': async (file) => {
const content = (await file.buffer()).toString();
const headings = content.match(/^#+\s+.+$/gm) || [];
console.log(`Markdown file ${file.path}: ${headings.length} headings`);
}
};
for (const file of directory.files) {
if (file.type === "File") {
const ext = path.extname(file.path).toLowerCase();
const processor = processors[ext];
if (processor) {
try {
await processor(file);
} catch (error) {
console.error(`Error processing ${file.path}:`, error.message);
}
}
}
}
};
await processArchiveByType("documentation.zip");const streamLargeFiles = async (zipPath, outputDir) => {
const directory = await unzipper.Open.file(zipPath);
// Find files larger than 1MB
const largeFiles = [];
for (const file of directory.files) {
if (file.type === "File") {
// For large files, avoid loading entire buffer
// Instead use stream processing
const stream = file.stream();
let size = 0;
await new Promise((resolve, reject) => {
stream.on('data', (chunk) => {
size += chunk.length;
});
stream.on('end', () => {
if (size > 1024 * 1024) { // 1MB
largeFiles.push({ file, size });
}
resolve();
});
stream.on('error', reject);
});
}
}
console.log(`Found ${largeFiles.length} large files`);
// Stream large files to disk
for (const { file, size } of largeFiles) {
console.log(`Streaming ${file.path} (${size} bytes)...`);
const outputPath = path.join(outputDir, file.path);
const outputDirPath = path.dirname(outputPath);
if (!fs.existsSync(outputDirPath)) {
fs.mkdirSync(outputDirPath, { recursive: true });
}
file.stream().pipe(fs.createWriteStream(outputPath));
}
};
await streamLargeFiles("large-archive.zip", "output");Install with Tessl CLI
npx tessl i tessl/npm-unzipper