@xhmikosr/decompress-unzip
Version:
decompress zip plugin
96 lines (73 loc) • 1.95 kB
JavaScript
/* eslint-disable no-bitwise */
import {Buffer} from 'node:buffer';
import {promisify} from 'node:util';
import {fileTypeFromBuffer} from 'file-type';
import {getStreamAsBuffer} from 'get-stream';
import yauzl from 'yauzl';
const getType = (entry, mode) => {
const IFMT = 61_440;
const IFDIR = 16_384;
const IFLNK = 40_960;
const madeBy = entry.versionMadeBy >> 8;
if ((mode & IFMT) === IFLNK) {
return 'symlink';
}
if ((mode & IFMT) === IFDIR || (madeBy === 0 && entry.externalFileAttributes === 16)) {
return 'directory';
}
return 'file';
};
const extractEntry = async (entry, zip) => {
const file = {
mode: (entry.externalFileAttributes >> 16) & 0xFF_FF,
mtime: entry.getLastModDate(),
path: entry.fileName,
};
file.type = getType(entry, file.mode);
if (file.mode === 0 && file.type === 'directory') {
file.mode = 493;
}
if (file.mode === 0) {
file.mode = 420;
}
try {
const stream = await promisify(zip.openReadStream.bind(zip))(entry);
const buf = await getStreamAsBuffer(stream);
file.data = buf;
if (file.type === 'symlink') {
file.linkname = buf.toString();
}
return file;
} catch (error) {
zip.close();
throw error;
}
};
const extractFile = zip => new Promise((resolve, reject) => {
const files = [];
zip.readEntry();
zip.on('entry', entry => {
extractEntry(entry, zip)
.catch(reject)
.then(file => {
files.push(file);
zip.readEntry();
});
});
zip.on('error', reject);
zip.on('end', () => resolve(files));
});
const decompressUnzip = () => async input => {
if (!Buffer.isBuffer(input)) {
throw new TypeError(`Expected a Buffer, got ${typeof input}`);
}
if (Buffer.isBuffer(input)) {
const type = await fileTypeFromBuffer(input);
if (!type || type.mime !== 'application/zip') {
return [];
}
}
const zip = await promisify(yauzl.fromBuffer)(input, {lazyEntries: true});
return extractFile(zip);
};
export default decompressUnzip;