Skip to content

Commit

Permalink
fix #52: async API
Browse files Browse the repository at this point in the history
  • Loading branch information
antelle committed Feb 5, 2021
1 parent eed57a1 commit 51e0ed9
Show file tree
Hide file tree
Showing 11 changed files with 384 additions and 98 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2015 Antelle https://github.com/antelle
Copyright (c) 2021 Antelle https://github.com/antelle

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Expand Down
108 changes: 95 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,99 @@ Features:
- deflate, sfx, macosx/windows built-in archives
- ZIP64 support

# Installation
## Installation

`$ npm install node-stream-zip`
```sh
npm i node-stream-zip
```

## Usage

# Usage
There are two APIs provided:
1. [promise-based / async](#async-api)
2. [callbacks](#callback-api)
It's recommended to use the new, promise API, however the legacy callback API
may be flexible for certain operations.

### Async API

Open a zip file
```javascript
const StreamZip = require('node-stream-zip');
const zip = new StreamZip({
file: 'archive.zip',
storeEntries: true
const zip = new StreamZip.async({ file: 'archive.zip' });
```

List entries
```javascript
const entriesCount = await zip.entriesCount;
console.log(`Entries read: ${entriesCount}`);

const entries = await zip.entries();
for (const entry of Object.values(entries)) {
const desc = entry.isDirectory ? 'directory' : `${entry.size} bytes`;
console.log(`Entry ${entry.name}: ${desc}`);
}

// Do not forget to close the file once you're done
await zip.close();
```

Stream one entry to stdout
```javascript
const srm = await zip.stream('path/inside/zip.txt');
stm.pipe(process.stdout);
stm.on('end', () => zip.close());
```

Extract one file to disk
```javascript
await zip.extract('path/inside/zip.txt', './extracted.txt');
await zip.close();
```

Extract a folder from archive to disk
```javascript
fs.mkdirSync('extracted');
await zip.extract('path/inside/zip/', './extracted');
zip.close();
```

Extract everything
```javascript
fs.mkdirSync('extracted');
const count = await zip.extract(null, './extracted');
console.log(`Extracted ${count} entries`);
await zip.close();
```

Read a file as buffer in sync way
```javascript
const data = await zip.entryData('path/inside/zip.txt');
await zip.close();
```

When extracting a folder, you can listen to `extract` event
```javascript
zip.on('extract', (entry, file) => {
console.log(`Extracted ${entry.name} to ${file}`);
});
```

`entry` event is generated for every entry during loading
```javascript
zip.on('entry', entry => {
// you can already stream this entry,
// without waiting until all entry descriptions are read (suitable for very large archives)
console.log(`Read entry ${entry.name}`);
});
```

### Callback API

Open a zip file
```javascript
const StreamZip = require('node-stream-zip');
const zip = new StreamZip({ file: 'archive.zip' });

// Handle errors
zip.on('error', err => { /*...*/ });
Expand All @@ -39,7 +119,7 @@ zip.on('ready', () => {
console.log(`Entry ${entry.name}: ${desc}`);
}
// Do not forget to close the file once you're done
zip.close()
zip.close();
});
```

Expand Down Expand Up @@ -109,30 +189,32 @@ zip.on('entry', entry => {
});
```

# Options
## Options

You can pass these options to the constructor
- `storeEntries: true` - you will be able to work with entries inside zip archive, otherwise the only way to access them is `entry` event
- `skipEntryNameValidation: true` - by default, entry name is checked for malicious characters, like `../` or `c:\123`, pass this flag to disable validation errors

# Methods
## Methods

- `zip.entries()` - get all entries description
- `zip.entry(name)` - get entry description by name
- `zip.stream(entry, function(err, stm) { })` - get entry data reader stream
- `zip.entryDataSync(entry)` - get entry data in sync way
- `zip.close()` - cleanup after all entries have been read, streamed, extracted, and you don't need the archive

# Building
## Building

The project doesn't require building. To run unit tests with [nodeunit](https://github.com/caolan/nodeunit):
`$ npm test`
```sh
npm test
```

# Known issues
## Known issues

- [utf8](https://github.com/rubyzip/rubyzip/wiki/Files-with-non-ascii-filenames) file names
- AES encrypted files

# Contributors
## Contributors

ZIP parsing code has been partially forked from [cthackers/adm-zip](https://github.com/cthackers/adm-zip) (MIT license).
35 changes: 35 additions & 0 deletions example-async.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint-disable no-console,no-unused-vars */
const StreamZip = require('./');

(async () => {
console.log('Loading zip...');

const zip = new StreamZip.async({ file: './test/ok/normal.zip' });
const entriesCount = await zip.entriesCount;

console.log(`Done in ${process.uptime()}s. Entries read: ${entriesCount}`);

const entry = await zip.entry('README.md');
console.log('Entry for README.md:', entry);

const data = await zip.entryData('README.md');
const firstLine = data.toString().split('\n')[0].trim();
console.log(`First line of README.md: "${firstLine}"`);

async function streamDataToStdOut() {
const stm = await zip.stream('README.md');
console.log('README.md contents streamed:\n');
stm.pipe(process.stdout);
}

async function extractEntry() {
await zip.extract('README.md', './tmp');
}

async function extractAll() {
const extracted = await zip.extract(null, './tmp');
console.log(`Extracted ${extracted} entries`);
}

await zip.close();
})().catch(console.error);
47 changes: 47 additions & 0 deletions example-callbacks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable no-console,no-unused-vars */
const StreamZip = require('./');

const zip = new StreamZip({ file: './test/ok/normal.zip' });
zip.on('error', (err) => {
console.error('ERROR: ' + err);
});
zip.on('ready', () => {
const entriesCount = zip.entriesCount;
console.log(`Done in ${process.uptime()}s. Entries read: ${entriesCount}`);

const entry = zip.entry('README.md');
console.log('Entry for README.md:', entry);

const data = zip.entryDataSync('README.md');
const firstLine = data.toString().split('\n')[0].trim();
console.log(`First line of README.md: "${firstLine}"`);

zip.close();

function streamDataToStdOut() {
zip.stream('README.md', (err, stm) => {
if (err) {
return console.error(err);
}
console.log('README.md contents streamed:\n');
stm.pipe(process.stdout);
});
}

function extractEntry() {
zip.extract('README.md', './tmp', (err) => {
console.log(err ? err : 'Entry extracted');
zip.close();
});
}

function extractAll() {
zip.extract(null, './tmp', (err, count) => {
console.log(err ? err : `Extracted ${count} entries`);
zip.close();
});
}
});
zip.on('extract', (entry, file) => {
console.log('extract', entry.name, file);
});
32 changes: 0 additions & 32 deletions example.js

This file was deleted.

Loading

0 comments on commit 51e0ed9

Please sign in to comment.