diff --git a/index.d.ts b/index.d.ts index 6dbcc80..74e169f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -11,11 +11,14 @@ declare namespace extract { defaultFileMode?: number; onEntry?: (entry: Entry, zipfile: ZipFile) => void; } + + type ExtractBuffer = (buffer: Buffer, opts: extract.Options) => Promise; + type ExtractFile = (zipPath: string, opts: extract.Options) => Promise; } -declare function extract( - zipPath: string, - opts: extract.Options, -): Promise; +declare const extract: extract.ExtractFile & { + extractBuffer: extract.ExtractBuffer, + extractFile: extract.ExtractFile +}; export = extract; diff --git a/index.js b/index.js index 23384ea..6fdf026 100644 --- a/index.js +++ b/index.js @@ -7,19 +7,33 @@ const { promisify } = require('util') const stream = require('stream') const yauzl = require('yauzl') +const fromBuffer = promisify(yauzl.fromBuffer) const openZip = promisify(yauzl.open) const pipeline = promisify(stream.pipeline) +const yauzlOpts = { lazyEntries: true } + class Extractor { - constructor (zipPath, opts) { - this.zipPath = zipPath + constructor (opts) { this.opts = opts } - async extract () { - debug('opening', this.zipPath, 'with opts', this.opts) + async extractFile (zipPath) { + debug('extracting file', zipPath, 'with opts', this.opts) + const zipfile = await openZip(zipPath, yauzlOpts) + return this.extract(zipfile) + } + + async extractBuffer (buffer) { + debug('extracting buffer with opts', this.opts) + const zipfile = await fromBuffer(buffer, yauzlOpts) + return this.extract(zipfile) + } - this.zipfile = await openZip(this.zipPath, { lazyEntries: true }) + async extract (zipfile) { + await this.ensureDir() + + this.zipfile = zipfile this.canceled = false return new Promise((resolve, reject) => { @@ -29,11 +43,9 @@ class Extractor { }) this.zipfile.readEntry() - this.zipfile.on('close', () => { - if (!this.canceled) { - debug('zip extraction complete') - resolve() - } + this.zipfile.on('end', () => { + debug('zip extraction complete') + resolve() }) this.zipfile.on('entry', async entry => { @@ -158,16 +170,27 @@ class Extractor { return mode } -} -module.exports = async function (zipPath, opts) { - debug('creating target directory', opts.dir) + async ensureDir () { + debug('creating target directory', this.opts.dir) - if (!path.isAbsolute(opts.dir)) { - throw new Error('Target directory is expected to be absolute') + if (!path.isAbsolute(this.opts.dir)) { + throw new Error('Target directory is expected to be absolute') + } + + await fs.mkdir(this.opts.dir, { recursive: true }) + this.opts.dir = await fs.realpath(this.opts.dir) } +} - await fs.mkdir(opts.dir, { recursive: true }) - opts.dir = await fs.realpath(opts.dir) - return new Extractor(zipPath, opts).extract() +async function extractFile (filename, opts) { + return new Extractor(opts).extractFile(filename) } + +async function extractBuffer (buffer, opts) { + return new Extractor(opts).extractBuffer(buffer) +} + +module.exports = extractFile +module.exports.extractBuffer = extractBuffer +module.exports.extractFile = extractFile diff --git a/readme.md b/readme.md index 4ee7108..a793113 100644 --- a/readme.md +++ b/readme.md @@ -31,7 +31,7 @@ const extract = require('extract-zip') async function main () { try { - await extract(source, { dir: target }) + await extract(filename, { dir: target }) console.log('Extraction complete') } catch (err) { // handle any errors @@ -39,6 +39,23 @@ async function main () { } ``` +The `extractBuffer` helper function can be used if the zipped data is +not stored in a file. It has the same `source` and `options` parameters: + +```javascript +const extract = require('extract-zip') + +async function main () { + try { + await extract.extractBuffer(buffer, { dir: target }) + console.log('Extraction complete') + } catch (err) { + // handle any errors + } +} +``` + + ### Options - `dir` (required) - the path to the directory where the extracted files are written diff --git a/test/index.js b/test/index.js index a955a21..e78c245 100644 --- a/test/index.js +++ b/test/index.js @@ -24,6 +24,12 @@ async function tempExtract (t, suffix, zipPath) { return dirPath } +async function tempExtractBuf (t, suffix, buffer) { + const dirPath = await mkdtemp(t, suffix) + await extract.extractBuffer(buffer, { dir: dirPath }) + return dirPath +} + async function pathExists (t, pathToCheck, message) { const exists = await fs.pathExists(pathToCheck) t.true(exists, message) @@ -45,6 +51,12 @@ test('files', async t => { await pathExists(t, path.join(dirPath, 'cats', 'gJqEYBs.jpg'), 'file created') }) +test('buffer', async t => { + const catsBuf = await fs.readFile(catsZip) + const dirPath = await tempExtractBuf(t, 'files', catsBuf) + await pathExists(t, path.join(dirPath, 'cats', 'gJqEYBs.jpg'), 'file created') +}) + test('symlinks', async t => { const dirPath = await tempExtract(t, 'symlinks', catsZip) const symlink = path.join(dirPath, 'cats', 'orange_symlink')