123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949 |
- const Utils = require("./util");
- const pth = require("path");
- const ZipEntry = require("./zipEntry");
- const ZipFile = require("./zipFile");
- const get_Bool = (...val) => Utils.findLast(val, (c) => typeof c === "boolean");
- const get_Str = (...val) => Utils.findLast(val, (c) => typeof c === "string");
- const get_Fun = (...val) => Utils.findLast(val, (c) => typeof c === "function");
- const defaultOptions = {
- // option "noSort" : if true it disables files sorting
- noSort: false,
- // read entries during load (initial loading may be slower)
- readEntries: false,
- // default method is none
- method: Utils.Constants.NONE,
- // file system
- fs: null
- };
- module.exports = function (/**String*/ input, /** object */ options) {
- let inBuffer = null;
- // create object based default options, allowing them to be overwritten
- const opts = Object.assign(Object.create(null), defaultOptions);
- // test input variable
- if (input && "object" === typeof input) {
- // if value is not buffer we accept it to be object with options
- if (!(input instanceof Uint8Array)) {
- Object.assign(opts, input);
- input = opts.input ? opts.input : undefined;
- if (opts.input) delete opts.input;
- }
- // if input is buffer
- if (Buffer.isBuffer(input)) {
- inBuffer = input;
- opts.method = Utils.Constants.BUFFER;
- input = undefined;
- }
- }
- // assign options
- Object.assign(opts, options);
- // instanciate utils filesystem
- const filetools = new Utils(opts);
- if (typeof opts.decoder !== "object" || typeof opts.decoder.encode !== "function" || typeof opts.decoder.decode !== "function") {
- opts.decoder = Utils.decoder;
- }
- // if input is file name we retrieve its content
- if (input && "string" === typeof input) {
- // load zip file
- if (filetools.fs.existsSync(input)) {
- opts.method = Utils.Constants.FILE;
- opts.filename = input;
- inBuffer = filetools.fs.readFileSync(input);
- } else {
- throw Utils.Errors.INVALID_FILENAME();
- }
- }
- // create variable
- const _zip = new ZipFile(inBuffer, opts);
- const { canonical, sanitize, zipnamefix } = Utils;
- function getEntry(/**Object*/ entry) {
- if (entry && _zip) {
- var item;
- // If entry was given as a file name
- if (typeof entry === "string") item = _zip.getEntry(pth.posix.normalize(entry));
- // if entry was given as a ZipEntry object
- if (typeof entry === "object" && typeof entry.entryName !== "undefined" && typeof entry.header !== "undefined") item = _zip.getEntry(entry.entryName);
- if (item) {
- return item;
- }
- }
- return null;
- }
- function fixPath(zipPath) {
- const { join, normalize, sep } = pth.posix;
- // convert windows file separators and normalize
- return join(".", normalize(sep + zipPath.split("\\").join(sep) + sep));
- }
- function filenameFilter(filterfn) {
- if (filterfn instanceof RegExp) {
- // if filter is RegExp wrap it
- return (function (rx) {
- return function (filename) {
- return rx.test(filename);
- };
- })(filterfn);
- } else if ("function" !== typeof filterfn) {
- // if filter is not function we will replace it
- return () => true;
- }
- return filterfn;
- }
- // keep last character on folders
- const relativePath = (local, entry) => {
- let lastChar = entry.slice(-1);
- lastChar = lastChar === filetools.sep ? filetools.sep : "";
- return pth.relative(local, entry) + lastChar;
- };
- return {
- /**
- * Extracts the given entry from the archive and returns the content as a Buffer object
- * @param {ZipEntry|string} entry ZipEntry object or String with the full path of the entry
- * @param {Buffer|string} [pass] - password
- * @return Buffer or Null in case of error
- */
- readFile: function (entry, pass) {
- var item = getEntry(entry);
- return (item && item.getData(pass)) || null;
- },
- /**
- * Returns how many child elements has on entry (directories) on files it is always 0
- * @param {ZipEntry|string} entry ZipEntry object or String with the full path of the entry
- * @returns {integer}
- */
- childCount: function (entry) {
- const item = getEntry(entry);
- if (item) {
- return _zip.getChildCount(item);
- }
- },
- /**
- * Asynchronous readFile
- * @param {ZipEntry|string} entry ZipEntry object or String with the full path of the entry
- * @param {callback} callback
- *
- * @return Buffer or Null in case of error
- */
- readFileAsync: function (entry, callback) {
- var item = getEntry(entry);
- if (item) {
- item.getDataAsync(callback);
- } else {
- callback(null, "getEntry failed for:" + entry);
- }
- },
- /**
- * Extracts the given entry from the archive and returns the content as plain text in the given encoding
- * @param {ZipEntry|string} entry - ZipEntry object or String with the full path of the entry
- * @param {string} encoding - Optional. If no encoding is specified utf8 is used
- *
- * @return String
- */
- readAsText: function (entry, encoding) {
- var item = getEntry(entry);
- if (item) {
- var data = item.getData();
- if (data && data.length) {
- return data.toString(encoding || "utf8");
- }
- }
- return "";
- },
- /**
- * Asynchronous readAsText
- * @param {ZipEntry|string} entry ZipEntry object or String with the full path of the entry
- * @param {callback} callback
- * @param {string} [encoding] - Optional. If no encoding is specified utf8 is used
- *
- * @return String
- */
- readAsTextAsync: function (entry, callback, encoding) {
- var item = getEntry(entry);
- if (item) {
- item.getDataAsync(function (data, err) {
- if (err) {
- callback(data, err);
- return;
- }
- if (data && data.length) {
- callback(data.toString(encoding || "utf8"));
- } else {
- callback("");
- }
- });
- } else {
- callback("");
- }
- },
- /**
- * Remove the entry from the file or the entry and all it's nested directories and files if the given entry is a directory
- *
- * @param {ZipEntry|string} entry
- * @returns {void}
- */
- deleteFile: function (entry, withsubfolders = true) {
- // @TODO: test deleteFile
- var item = getEntry(entry);
- if (item) {
- _zip.deleteFile(item.entryName, withsubfolders);
- }
- },
- /**
- * Remove the entry from the file or directory without affecting any nested entries
- *
- * @param {ZipEntry|string} entry
- * @returns {void}
- */
- deleteEntry: function (entry) {
- // @TODO: test deleteEntry
- var item = getEntry(entry);
- if (item) {
- _zip.deleteEntry(item.entryName);
- }
- },
- /**
- * Adds a comment to the zip. The zip must be rewritten after adding the comment.
- *
- * @param {string} comment
- */
- addZipComment: function (comment) {
- // @TODO: test addZipComment
- _zip.comment = comment;
- },
- /**
- * Returns the zip comment
- *
- * @return String
- */
- getZipComment: function () {
- return _zip.comment || "";
- },
- /**
- * Adds a comment to a specified zipEntry. The zip must be rewritten after adding the comment
- * The comment cannot exceed 65535 characters in length
- *
- * @param {ZipEntry} entry
- * @param {string} comment
- */
- addZipEntryComment: function (entry, comment) {
- var item = getEntry(entry);
- if (item) {
- item.comment = comment;
- }
- },
- /**
- * Returns the comment of the specified entry
- *
- * @param {ZipEntry} entry
- * @return String
- */
- getZipEntryComment: function (entry) {
- var item = getEntry(entry);
- if (item) {
- return item.comment || "";
- }
- return "";
- },
- /**
- * Updates the content of an existing entry inside the archive. The zip must be rewritten after updating the content
- *
- * @param {ZipEntry} entry
- * @param {Buffer} content
- */
- updateFile: function (entry, content) {
- var item = getEntry(entry);
- if (item) {
- item.setData(content);
- }
- },
- /**
- * Adds a file from the disk to the archive
- *
- * @param {string} localPath File to add to zip
- * @param {string} [zipPath] Optional path inside the zip
- * @param {string} [zipName] Optional name for the file
- * @param {string} [comment] Optional file comment
- */
- addLocalFile: function (localPath, zipPath, zipName, comment) {
- if (filetools.fs.existsSync(localPath)) {
- // fix ZipPath
- zipPath = zipPath ? fixPath(zipPath) : "";
- // p - local file name
- const p = pth.win32.basename(pth.win32.normalize(localPath));
- // add file name into zippath
- zipPath += zipName ? zipName : p;
- // read file attributes
- const _attr = filetools.fs.statSync(localPath);
- // get file content
- const data = _attr.isFile() ? filetools.fs.readFileSync(localPath) : Buffer.alloc(0);
- // if folder
- if (_attr.isDirectory()) zipPath += filetools.sep;
- // add file into zip file
- this.addFile(zipPath, data, comment, _attr);
- } else {
- throw Utils.Errors.FILE_NOT_FOUND(localPath);
- }
- },
- /**
- * Callback for showing if everything was done.
- *
- * @callback doneCallback
- * @param {Error} err - Error object
- * @param {boolean} done - was request fully completed
- */
- /**
- * Adds a file from the disk to the archive
- *
- * @param {(object|string)} options - options object, if it is string it us used as localPath.
- * @param {string} options.localPath - Local path to the file.
- * @param {string} [options.comment] - Optional file comment.
- * @param {string} [options.zipPath] - Optional path inside the zip
- * @param {string} [options.zipName] - Optional name for the file
- * @param {doneCallback} callback - The callback that handles the response.
- */
- addLocalFileAsync: function (options, callback) {
- options = typeof options === "object" ? options : { localPath: options };
- const localPath = pth.resolve(options.localPath);
- const { comment } = options;
- let { zipPath, zipName } = options;
- const self = this;
- filetools.fs.stat(localPath, function (err, stats) {
- if (err) return callback(err, false);
- // fix ZipPath
- zipPath = zipPath ? fixPath(zipPath) : "";
- // p - local file name
- const p = pth.win32.basename(pth.win32.normalize(localPath));
- // add file name into zippath
- zipPath += zipName ? zipName : p;
- if (stats.isFile()) {
- filetools.fs.readFile(localPath, function (err, data) {
- if (err) return callback(err, false);
- self.addFile(zipPath, data, comment, stats);
- return setImmediate(callback, undefined, true);
- });
- } else if (stats.isDirectory()) {
- zipPath += filetools.sep;
- self.addFile(zipPath, Buffer.alloc(0), comment, stats);
- return setImmediate(callback, undefined, true);
- }
- });
- },
- /**
- * Adds a local directory and all its nested files and directories to the archive
- *
- * @param {string} localPath - local path to the folder
- * @param {string} [zipPath] - optional path inside zip
- * @param {(RegExp|function)} [filter] - optional RegExp or Function if files match will be included.
- */
- addLocalFolder: function (localPath, zipPath, filter) {
- // Prepare filter
- filter = filenameFilter(filter);
- // fix ZipPath
- zipPath = zipPath ? fixPath(zipPath) : "";
- // normalize the path first
- localPath = pth.normalize(localPath);
- if (filetools.fs.existsSync(localPath)) {
- const items = filetools.findFiles(localPath);
- const self = this;
- if (items.length) {
- for (const filepath of items) {
- const p = pth.join(zipPath, relativePath(localPath, filepath));
- if (filter(p)) {
- self.addLocalFile(filepath, pth.dirname(p));
- }
- }
- }
- } else {
- throw Utils.Errors.FILE_NOT_FOUND(localPath);
- }
- },
- /**
- * Asynchronous addLocalFolder
- * @param {string} localPath
- * @param {callback} callback
- * @param {string} [zipPath] optional path inside zip
- * @param {RegExp|function} [filter] optional RegExp or Function if files match will
- * be included.
- */
- addLocalFolderAsync: function (localPath, callback, zipPath, filter) {
- // Prepare filter
- filter = filenameFilter(filter);
- // fix ZipPath
- zipPath = zipPath ? fixPath(zipPath) : "";
- // normalize the path first
- localPath = pth.normalize(localPath);
- var self = this;
- filetools.fs.open(localPath, "r", function (err) {
- if (err && err.code === "ENOENT") {
- callback(undefined, Utils.Errors.FILE_NOT_FOUND(localPath));
- } else if (err) {
- callback(undefined, err);
- } else {
- var items = filetools.findFiles(localPath);
- var i = -1;
- var next = function () {
- i += 1;
- if (i < items.length) {
- var filepath = items[i];
- var p = relativePath(localPath, filepath).split("\\").join("/"); //windows fix
- p = p
- .normalize("NFD")
- .replace(/[\u0300-\u036f]/g, "")
- .replace(/[^\x20-\x7E]/g, ""); // accent fix
- if (filter(p)) {
- filetools.fs.stat(filepath, function (er0, stats) {
- if (er0) callback(undefined, er0);
- if (stats.isFile()) {
- filetools.fs.readFile(filepath, function (er1, data) {
- if (er1) {
- callback(undefined, er1);
- } else {
- self.addFile(zipPath + p, data, "", stats);
- next();
- }
- });
- } else {
- self.addFile(zipPath + p + "/", Buffer.alloc(0), "", stats);
- next();
- }
- });
- } else {
- process.nextTick(() => {
- next();
- });
- }
- } else {
- callback(true, undefined);
- }
- };
- next();
- }
- });
- },
- /**
- * Adds a local directory and all its nested files and directories to the archive
- *
- * @param {object | string} options - options object, if it is string it us used as localPath.
- * @param {string} options.localPath - Local path to the folder.
- * @param {string} [options.zipPath] - optional path inside zip.
- * @param {RegExp|function} [options.filter] - optional RegExp or Function if files match will be included.
- * @param {function|string} [options.namefix] - optional function to help fix filename
- * @param {doneCallback} callback - The callback that handles the response.
- *
- */
- addLocalFolderAsync2: function (options, callback) {
- const self = this;
- options = typeof options === "object" ? options : { localPath: options };
- localPath = pth.resolve(fixPath(options.localPath));
- let { zipPath, filter, namefix } = options;
- if (filter instanceof RegExp) {
- filter = (function (rx) {
- return function (filename) {
- return rx.test(filename);
- };
- })(filter);
- } else if ("function" !== typeof filter) {
- filter = function () {
- return true;
- };
- }
- // fix ZipPath
- zipPath = zipPath ? fixPath(zipPath) : "";
- // Check Namefix function
- if (namefix == "latin1") {
- namefix = (str) =>
- str
- .normalize("NFD")
- .replace(/[\u0300-\u036f]/g, "")
- .replace(/[^\x20-\x7E]/g, ""); // accent fix (latin1 characers only)
- }
- if (typeof namefix !== "function") namefix = (str) => str;
- // internal, create relative path + fix the name
- const relPathFix = (entry) => pth.join(zipPath, namefix(relativePath(localPath, entry)));
- const fileNameFix = (entry) => pth.win32.basename(pth.win32.normalize(namefix(entry)));
- filetools.fs.open(localPath, "r", function (err) {
- if (err && err.code === "ENOENT") {
- callback(undefined, Utils.Errors.FILE_NOT_FOUND(localPath));
- } else if (err) {
- callback(undefined, err);
- } else {
- filetools.findFilesAsync(localPath, function (err, fileEntries) {
- if (err) return callback(err);
- fileEntries = fileEntries.filter((dir) => filter(relPathFix(dir)));
- if (!fileEntries.length) callback(undefined, false);
- setImmediate(
- fileEntries.reverse().reduce(function (next, entry) {
- return function (err, done) {
- if (err || done === false) return setImmediate(next, err, false);
- self.addLocalFileAsync(
- {
- localPath: entry,
- zipPath: pth.dirname(relPathFix(entry)),
- zipName: fileNameFix(entry)
- },
- next
- );
- };
- }, callback)
- );
- });
- }
- });
- },
- /**
- * Adds a local directory and all its nested files and directories to the archive
- *
- * @param {string} localPath - path where files will be extracted
- * @param {object} props - optional properties
- * @param {string} [props.zipPath] - optional path inside zip
- * @param {RegExp|function} [props.filter] - optional RegExp or Function if files match will be included.
- * @param {function|string} [props.namefix] - optional function to help fix filename
- */
- addLocalFolderPromise: function (localPath, props) {
- return new Promise((resolve, reject) => {
- this.addLocalFolderAsync2(Object.assign({ localPath }, props), (err, done) => {
- if (err) reject(err);
- if (done) resolve(this);
- });
- });
- },
- /**
- * Allows you to create a entry (file or directory) in the zip file.
- * If you want to create a directory the entryName must end in / and a null buffer should be provided.
- * Comment and attributes are optional
- *
- * @param {string} entryName
- * @param {Buffer | string} content - file content as buffer or utf8 coded string
- * @param {string} [comment] - file comment
- * @param {number | object} [attr] - number as unix file permissions, object as filesystem Stats object
- */
- addFile: function (entryName, content, comment, attr) {
- entryName = zipnamefix(entryName);
- let entry = getEntry(entryName);
- const update = entry != null;
- // prepare new entry
- if (!update) {
- entry = new ZipEntry(opts);
- entry.entryName = entryName;
- }
- entry.comment = comment || "";
- const isStat = "object" === typeof attr && attr instanceof filetools.fs.Stats;
- // last modification time from file stats
- if (isStat) {
- entry.header.time = attr.mtime;
- }
- // Set file attribute
- var fileattr = entry.isDirectory ? 0x10 : 0; // (MS-DOS directory flag)
- // extended attributes field for Unix
- // set file type either S_IFDIR / S_IFREG
- let unix = entry.isDirectory ? 0x4000 : 0x8000;
- if (isStat) {
- // File attributes from file stats
- unix |= 0xfff & attr.mode;
- } else if ("number" === typeof attr) {
- // attr from given attr values
- unix |= 0xfff & attr;
- } else {
- // Default values:
- unix |= entry.isDirectory ? 0o755 : 0o644; // permissions (drwxr-xr-x) or (-r-wr--r--)
- }
- fileattr = (fileattr | (unix << 16)) >>> 0; // add attributes
- entry.attr = fileattr;
- entry.setData(content);
- if (!update) _zip.setEntry(entry);
- return entry;
- },
- /**
- * Returns an array of ZipEntry objects representing the files and folders inside the archive
- *
- * @param {string} [password]
- * @returns Array
- */
- getEntries: function (password) {
- _zip.password = password;
- return _zip ? _zip.entries : [];
- },
- /**
- * Returns a ZipEntry object representing the file or folder specified by ``name``.
- *
- * @param {string} name
- * @return ZipEntry
- */
- getEntry: function (/**String*/ name) {
- return getEntry(name);
- },
- getEntryCount: function () {
- return _zip.getEntryCount();
- },
- forEach: function (callback) {
- return _zip.forEach(callback);
- },
- /**
- * Extracts the given entry to the given targetPath
- * If the entry is a directory inside the archive, the entire directory and it's subdirectories will be extracted
- *
- * @param {string|ZipEntry} entry - ZipEntry object or String with the full path of the entry
- * @param {string} targetPath - Target folder where to write the file
- * @param {boolean} [maintainEntryPath=true] - If maintainEntryPath is true and the entry is inside a folder, the entry folder will be created in targetPath as well. Default is TRUE
- * @param {boolean} [overwrite=false] - If the file already exists at the target path, the file will be overwriten if this is true.
- * @param {boolean} [keepOriginalPermission=false] - The file will be set as the permission from the entry if this is true.
- * @param {string} [outFileName] - String If set will override the filename of the extracted file (Only works if the entry is a file)
- *
- * @return Boolean
- */
- extractEntryTo: function (entry, targetPath, maintainEntryPath, overwrite, keepOriginalPermission, outFileName) {
- overwrite = get_Bool(false, overwrite);
- keepOriginalPermission = get_Bool(false, keepOriginalPermission);
- maintainEntryPath = get_Bool(true, maintainEntryPath);
- outFileName = get_Str(keepOriginalPermission, outFileName);
- var item = getEntry(entry);
- if (!item) {
- throw Utils.Errors.NO_ENTRY();
- }
- var entryName = canonical(item.entryName);
- var target = sanitize(targetPath, outFileName && !item.isDirectory ? outFileName : maintainEntryPath ? entryName : pth.basename(entryName));
- if (item.isDirectory) {
- var children = _zip.getEntryChildren(item);
- children.forEach(function (child) {
- if (child.isDirectory) return;
- var content = child.getData();
- if (!content) {
- throw Utils.Errors.CANT_EXTRACT_FILE();
- }
- var name = canonical(child.entryName);
- var childName = sanitize(targetPath, maintainEntryPath ? name : pth.basename(name));
- // The reverse operation for attr depend on method addFile()
- const fileAttr = keepOriginalPermission ? child.header.fileAttr : undefined;
- filetools.writeFileTo(childName, content, overwrite, fileAttr);
- });
- return true;
- }
- var content = item.getData(_zip.password);
- if (!content) throw Utils.Errors.CANT_EXTRACT_FILE();
- if (filetools.fs.existsSync(target) && !overwrite) {
- throw Utils.Errors.CANT_OVERRIDE();
- }
- // The reverse operation for attr depend on method addFile()
- const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
- filetools.writeFileTo(target, content, overwrite, fileAttr);
- return true;
- },
- /**
- * Test the archive
- * @param {string} [pass]
- */
- test: function (pass) {
- if (!_zip) {
- return false;
- }
- for (var entry in _zip.entries) {
- try {
- if (entry.isDirectory) {
- continue;
- }
- var content = _zip.entries[entry].getData(pass);
- if (!content) {
- return false;
- }
- } catch (err) {
- return false;
- }
- }
- return true;
- },
- /**
- * Extracts the entire archive to the given location
- *
- * @param {string} targetPath Target location
- * @param {boolean} [overwrite=false] If the file already exists at the target path, the file will be overwriten if this is true.
- * Default is FALSE
- * @param {boolean} [keepOriginalPermission=false] The file will be set as the permission from the entry if this is true.
- * Default is FALSE
- * @param {string|Buffer} [pass] password
- */
- extractAllTo: function (targetPath, overwrite, keepOriginalPermission, pass) {
- keepOriginalPermission = get_Bool(false, keepOriginalPermission);
- pass = get_Str(keepOriginalPermission, pass);
- overwrite = get_Bool(false, overwrite);
- if (!_zip) throw Utils.Errors.NO_ZIP();
- _zip.entries.forEach(function (entry) {
- var entryName = sanitize(targetPath, canonical(entry.entryName));
- if (entry.isDirectory) {
- filetools.makeDir(entryName);
- return;
- }
- var content = entry.getData(pass);
- if (!content) {
- throw Utils.Errors.CANT_EXTRACT_FILE();
- }
- // The reverse operation for attr depend on method addFile()
- const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
- filetools.writeFileTo(entryName, content, overwrite, fileAttr);
- try {
- filetools.fs.utimesSync(entryName, entry.header.time, entry.header.time);
- } catch (err) {
- throw Utils.Errors.CANT_EXTRACT_FILE();
- }
- });
- },
- /**
- * Asynchronous extractAllTo
- *
- * @param {string} targetPath Target location
- * @param {boolean} [overwrite=false] If the file already exists at the target path, the file will be overwriten if this is true.
- * Default is FALSE
- * @param {boolean} [keepOriginalPermission=false] The file will be set as the permission from the entry if this is true.
- * Default is FALSE
- * @param {function} callback The callback will be executed when all entries are extracted successfully or any error is thrown.
- */
- extractAllToAsync: function (targetPath, overwrite, keepOriginalPermission, callback) {
- callback = get_Fun(overwrite, keepOriginalPermission, callback);
- keepOriginalPermission = get_Bool(false, keepOriginalPermission);
- overwrite = get_Bool(false, overwrite);
- if (!callback) {
- return new Promise((resolve, reject) => {
- this.extractAllToAsync(targetPath, overwrite, keepOriginalPermission, function (err) {
- if (err) {
- reject(err);
- } else {
- resolve(this);
- }
- });
- });
- }
- if (!_zip) {
- callback(Utils.Errors.NO_ZIP());
- return;
- }
- targetPath = pth.resolve(targetPath);
- // convert entryName to
- const getPath = (entry) => sanitize(targetPath, pth.normalize(canonical(entry.entryName)));
- const getError = (msg, file) => new Error(msg + ': "' + file + '"');
- // separate directories from files
- const dirEntries = [];
- const fileEntries = [];
- _zip.entries.forEach((e) => {
- if (e.isDirectory) {
- dirEntries.push(e);
- } else {
- fileEntries.push(e);
- }
- });
- // Create directory entries first synchronously
- // this prevents race condition and assures folders are there before writing files
- for (const entry of dirEntries) {
- const dirPath = getPath(entry);
- // The reverse operation for attr depend on method addFile()
- const dirAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
- try {
- filetools.makeDir(dirPath);
- if (dirAttr) filetools.fs.chmodSync(dirPath, dirAttr);
- // in unix timestamp will change if files are later added to folder, but still
- filetools.fs.utimesSync(dirPath, entry.header.time, entry.header.time);
- } catch (er) {
- callback(getError("Unable to create folder", dirPath));
- }
- }
- fileEntries.reverse().reduce(function (next, entry) {
- return function (err) {
- if (err) {
- next(err);
- } else {
- const entryName = pth.normalize(canonical(entry.entryName));
- const filePath = sanitize(targetPath, entryName);
- entry.getDataAsync(function (content, err_1) {
- if (err_1) {
- next(err_1);
- } else if (!content) {
- next(Utils.Errors.CANT_EXTRACT_FILE());
- } else {
- // The reverse operation for attr depend on method addFile()
- const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined;
- filetools.writeFileToAsync(filePath, content, overwrite, fileAttr, function (succ) {
- if (!succ) {
- next(getError("Unable to write file", filePath));
- }
- filetools.fs.utimes(filePath, entry.header.time, entry.header.time, function (err_2) {
- if (err_2) {
- next(getError("Unable to set times", filePath));
- } else {
- next();
- }
- });
- });
- }
- });
- }
- };
- }, callback)();
- },
- /**
- * Writes the newly created zip file to disk at the specified location or if a zip was opened and no ``targetFileName`` is provided, it will overwrite the opened zip
- *
- * @param {string} targetFileName
- * @param {function} callback
- */
- writeZip: function (targetFileName, callback) {
- if (arguments.length === 1) {
- if (typeof targetFileName === "function") {
- callback = targetFileName;
- targetFileName = "";
- }
- }
- if (!targetFileName && opts.filename) {
- targetFileName = opts.filename;
- }
- if (!targetFileName) return;
- var zipData = _zip.compressToBuffer();
- if (zipData) {
- var ok = filetools.writeFileTo(targetFileName, zipData, true);
- if (typeof callback === "function") callback(!ok ? new Error("failed") : null, "");
- }
- },
- /**
- *
- * @param {string} targetFileName
- * @param {object} [props]
- * @param {boolean} [props.overwrite=true] If the file already exists at the target path, the file will be overwriten if this is true.
- * @param {boolean} [props.perm] The file will be set as the permission from the entry if this is true.
- * @returns {Promise<void>}
- */
- writeZipPromise: function (/**String*/ targetFileName, /* object */ props) {
- const { overwrite, perm } = Object.assign({ overwrite: true }, props);
- return new Promise((resolve, reject) => {
- // find file name
- if (!targetFileName && opts.filename) targetFileName = opts.filename;
- if (!targetFileName) reject("ADM-ZIP: ZIP File Name Missing");
- this.toBufferPromise().then((zipData) => {
- const ret = (done) => (done ? resolve(done) : reject("ADM-ZIP: Wasn't able to write zip file"));
- filetools.writeFileToAsync(targetFileName, zipData, overwrite, perm, ret);
- }, reject);
- });
- },
- /**
- * @returns {Promise<Buffer>} A promise to the Buffer.
- */
- toBufferPromise: function () {
- return new Promise((resolve, reject) => {
- _zip.toAsyncBuffer(resolve, reject);
- });
- },
- /**
- * Returns the content of the entire zip file as a Buffer object
- *
- * @prop {function} [onSuccess]
- * @prop {function} [onFail]
- * @prop {function} [onItemStart]
- * @prop {function} [onItemEnd]
- * @returns {Buffer}
- */
- toBuffer: function (onSuccess, onFail, onItemStart, onItemEnd) {
- if (typeof onSuccess === "function") {
- _zip.toAsyncBuffer(onSuccess, onFail, onItemStart, onItemEnd);
- return null;
- }
- return _zip.compressToBuffer();
- }
- };
- };
|