mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-18 04:26:01 +00:00
92 lines
3.0 KiB
JavaScript
92 lines
3.0 KiB
JavaScript
const yauzl = require('yauzl');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const { getLogger, extractErrorLogData } = require('dbgate-tools');
|
||
|
||
const logger = getLogger('unzipDirectory');
|
||
|
||
/**
|
||
* Extracts an entire ZIP file, preserving its internal directory layout.
|
||
*
|
||
* @param {string} zipPath Path to the ZIP file on disk.
|
||
* @param {string} outputDirectory Folder to create / overwrite with the contents.
|
||
* @returns {Promise<boolean>} Resolves `true` on success, rejects on error.
|
||
*/
|
||
function unzipDirectory(zipPath, outputDirectory) {
|
||
return new Promise((resolve, reject) => {
|
||
yauzl.open(zipPath, { lazyEntries: true }, (err, zipFile) => {
|
||
if (err) return reject(err);
|
||
|
||
/** Pending per-file extractions – we resolve the main promise after they’re all done */
|
||
const pending = [];
|
||
|
||
// kick things off
|
||
zipFile.readEntry();
|
||
|
||
zipFile.on('entry', entry => {
|
||
const destPath = path.join(outputDirectory, entry.fileName);
|
||
|
||
// Handle directories (their names always end with “/” in ZIPs)
|
||
if (/\/$/.test(entry.fileName)) {
|
||
// Ensure directory exists, then continue to next entry
|
||
fs.promises
|
||
.mkdir(destPath, { recursive: true })
|
||
.then(() => zipFile.readEntry())
|
||
.catch(reject);
|
||
return;
|
||
}
|
||
|
||
// Handle files
|
||
const filePromise = fs.promises
|
||
.mkdir(path.dirname(destPath), { recursive: true }) // make sure parent dirs exist
|
||
.then(
|
||
() =>
|
||
new Promise((res, rej) => {
|
||
zipFile.openReadStream(entry, (err, readStream) => {
|
||
if (err) return rej(err);
|
||
|
||
const writeStream = fs.createWriteStream(destPath);
|
||
readStream.pipe(writeStream);
|
||
|
||
// proceed to next entry once we’ve consumed *this* one
|
||
readStream.on('end', () => zipFile.readEntry());
|
||
|
||
writeStream.on('finish', () => {
|
||
logger.info(`Extracted "${entry.fileName}" → "${destPath}".`);
|
||
res();
|
||
});
|
||
|
||
writeStream.on('error', writeErr => {
|
||
logger.error(
|
||
extractErrorLogData(writeErr),
|
||
`Error extracting "${entry.fileName}" from "${zipPath}".`
|
||
);
|
||
rej(writeErr);
|
||
});
|
||
});
|
||
})
|
||
);
|
||
|
||
pending.push(filePromise);
|
||
});
|
||
|
||
// Entire archive enumerated; wait for all streams to finish
|
||
zipFile.on('end', () => {
|
||
Promise.all(pending)
|
||
.then(() => {
|
||
logger.info(`Archive "${zipPath}" fully extracted to "${outputDirectory}".`);
|
||
resolve(true);
|
||
})
|
||
.catch(reject);
|
||
});
|
||
|
||
zipFile.on('error', err => {
|
||
logger.error(extractErrorLogData(err), `ZIP file error in ${zipPath}.`);
|
||
reject(err);
|
||
});
|
||
});
|
||
});
|
||
}
|
||
|
||
module.exports = unzipDirectory;
|