222 lines
5.7 KiB
Markdown
222 lines
5.7 KiB
Markdown
![]() |
# node-stream-zip 
|
||
|
|
||
|
node.js library for reading and extraction of ZIP archives.
|
||
|
Features:
|
||
|
|
||
|
- it never loads entire archive into memory, everything is read by chunks
|
||
|
- large archives support
|
||
|
- all operations are non-blocking, no sync i/o
|
||
|
- fast initialization
|
||
|
- no dependencies, no binary addons
|
||
|
- decompression with built-in zlib module
|
||
|
- deflate, sfx, macosx/windows built-in archives
|
||
|
- ZIP64 support
|
||
|
|
||
|
## Installation
|
||
|
|
||
|
```sh
|
||
|
npm i node-stream-zip
|
||
|
```
|
||
|
|
||
|
## 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 more flexible for certain operations.
|
||
|
|
||
|
### Async API
|
||
|
|
||
|
Open a zip file
|
||
|
```javascript
|
||
|
const StreamZip = require('node-stream-zip');
|
||
|
const zip = new StreamZip.async({ file: 'archive.zip' });
|
||
|
```
|
||
|
|
||
|
Stream one entry to stdout
|
||
|
```javascript
|
||
|
const stm = await zip.stream('path/inside/zip.txt');
|
||
|
stm.pipe(process.stdout);
|
||
|
stm.on('end', () => zip.close());
|
||
|
```
|
||
|
|
||
|
Read a file as buffer
|
||
|
```javascript
|
||
|
const data = await zip.entryData('path/inside/zip.txt');
|
||
|
await zip.close();
|
||
|
```
|
||
|
|
||
|
Extract one file to disk
|
||
|
```javascript
|
||
|
await zip.extract('path/inside/zip.txt', './extracted.txt');
|
||
|
await zip.close();
|
||
|
```
|
||
|
|
||
|
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();
|
||
|
```
|
||
|
|
||
|
Extract a folder from archive to disk
|
||
|
```javascript
|
||
|
fs.mkdirSync('extracted');
|
||
|
await zip.extract('path/inside/zip/', './extracted');
|
||
|
await zip.close();
|
||
|
```
|
||
|
|
||
|
Extract everything
|
||
|
```javascript
|
||
|
fs.mkdirSync('extracted');
|
||
|
const count = await zip.extract(null, './extracted');
|
||
|
console.log(`Extracted ${count} entries`);
|
||
|
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 => { /*...*/ });
|
||
|
```
|
||
|
|
||
|
List entries
|
||
|
```javascript
|
||
|
zip.on('ready', () => {
|
||
|
console.log('Entries read: ' + zip.entriesCount);
|
||
|
for (const entry of Object.values(zip.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
|
||
|
zip.close();
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Stream one entry to stdout
|
||
|
```javascript
|
||
|
zip.on('ready', () => {
|
||
|
zip.stream('path/inside/zip.txt', (err, stm) => {
|
||
|
stm.pipe(process.stdout);
|
||
|
stm.on('end', () => zip.close());
|
||
|
});
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Extract one file to disk
|
||
|
```javascript
|
||
|
zip.on('ready', () => {
|
||
|
zip.extract('path/inside/zip.txt', './extracted.txt', err => {
|
||
|
console.log(err ? 'Extract error' : 'Extracted');
|
||
|
zip.close();
|
||
|
});
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Extract a folder from archive to disk
|
||
|
```javascript
|
||
|
zip.on('ready', () => {
|
||
|
fs.mkdirSync('extracted');
|
||
|
zip.extract('path/inside/zip/', './extracted', err => {
|
||
|
console.log(err ? 'Extract error' : 'Extracted');
|
||
|
zip.close();
|
||
|
});
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Extract everything
|
||
|
```javascript
|
||
|
zip.on('ready', () => {
|
||
|
fs.mkdirSync('extracted');
|
||
|
zip.extract(null, './extracted', (err, count) => {
|
||
|
console.log(err ? 'Extract error' : `Extracted ${count} entries`);
|
||
|
zip.close();
|
||
|
});
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Read a file as buffer in sync way
|
||
|
```javascript
|
||
|
zip.on('ready', () => {
|
||
|
const data = zip.entryDataSync('path/inside/zip.txt');
|
||
|
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}`);
|
||
|
});
|
||
|
```
|
||
|
|
||
|
## 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
|
||
|
|
||
|
- `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
|
||
|
|
||
|
The project doesn't require building. To run unit tests with [nodeunit](https://github.com/caolan/nodeunit):
|
||
|
```sh
|
||
|
npm test
|
||
|
```
|
||
|
|
||
|
## Known issues
|
||
|
|
||
|
- [utf8](https://github.com/rubyzip/rubyzip/wiki/Files-with-non-ascii-filenames) file names
|
||
|
- AES encrypted files
|
||
|
|
||
|
## Contributors
|
||
|
|
||
|
ZIP parsing code has been partially forked from [cthackers/adm-zip](https://github.com/cthackers/adm-zip) (MIT license).
|