var fs = require("fs"), pth = require("path"); fs.existsSync = fs.existsSync || pth.existsSync; var ZipEntry = require("./zipEntry"), ZipFile = require("./zipFile"), Utils = require("./util"); module.exports = function(/*String*/input) { var _zip = undefined, _filename = ""; if (input && typeof input === "string") { // load zip file if (fs.existsSync(input)) { _filename = input; _zip = new ZipFile(input, Utils.Constants.FILE); } else { throw Utils.Errors.INVALID_FILENAME; } } else if(input && Buffer.isBuffer(input)) { // load buffer _zip = new ZipFile(input, Utils.Constants.BUFFER); } else { // create new zip file _zip = new ZipFile(null, Utils.Constants.NONE); } function getEntry(/*Object*/entry) { if (entry && _zip) { var item; // If entry was given as a file name if (typeof entry === "string") item = _zip.getEntry(entry); // if entry was given as a ZipEntry object if (typeof entry === "object" && entry.entryName != undefined && entry.header != undefined) item = _zip.getEntry(entry.entryName); if (item) { return item; } } return null; } return { /** * Extracts the given entry from the archive and returns the content as a Buffer object * @param entry ZipEntry object or String with the full path of the entry * * @return Buffer or Null in case of error */ readFile : function(/*Object*/entry) { var item = getEntry(entry); return item && item.getData() || null; }, /** * Asynchronous readFile * @param entry ZipEntry object or String with the full path of the entry * @param callback * * @return Buffer or Null in case of error */ readFileAsync : function(/*Object*/entry, /*Function*/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 entry ZipEntry object or String with the full path of the entry * @param encoding Optional. If no encoding is specified utf8 is used * * @return String */ readAsText : function(/*Object*/entry, /*String - Optional*/encoding) { var item = getEntry(entry); if (item) { var data = item.getData(); if (data && data.length) { return data.toString(encoding || "utf8"); } } return ""; }, /** * Asynchronous readAsText * @param entry ZipEntry object or String with the full path of the entry * @param callback * @param encoding Optional. If no encoding is specified utf8 is used * * @return String */ readAsTextAsync : function(/*Object*/entry, /*Function*/callback, /*String - Optional*/encoding) { var item = getEntry(entry); if (item) { item.getDataAsync(function(data) { 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 entry */ deleteFile : function(/*Object*/entry) { // @TODO: test deleteFile 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 comment */ addZipComment : function(/*String*/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 entry * @param comment */ addZipEntryComment : function(/*Object*/entry,/*String*/comment) { var item = getEntry(entry); if (item) { item.comment = comment; } }, /** * Returns the comment of the specified entry * * @param entry * @return String */ getZipEntryComment : function(/*Object*/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 entry * @param content */ updateFile : function(/*Object*/entry, /*Buffer*/content) { var item = getEntry(entry); if (item) { item.setData(content); } }, /** * Adds a file from the disk to the archive * * @param localPath */ addLocalFile : function(/*String*/localPath, /*String*/zipPath) { if (fs.existsSync(localPath)) { if(zipPath){ zipPath=zipPath.split("\\").join("/"); if(zipPath.charAt(zipPath.length - 1) != "/"){ zipPath += "/"; } }else{ zipPath=""; } var p = localPath.split("\\").join("/").split("/").pop(); this.addFile(zipPath+p, fs.readFileSync(localPath), "", 0) } else { throw Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath); } }, /** * Adds a local directory and all its nested files and directories to the archive * * @param localPath */ addLocalFolder : function(/*String*/localPath, /*String*/zipPath) { if(zipPath){ zipPath=zipPath.split("\\").join("/"); if(zipPath.charAt(zipPath.length - 1) != "/"){ zipPath += "/"; } }else{ zipPath=""; } localPath = localPath.split("\\").join("/"); //windows fix if (localPath.charAt(localPath.length - 1) != "/") localPath += "/"; if (fs.existsSync(localPath)) { var items = Utils.findFiles(localPath), self = this; if (items.length) { items.forEach(function(path) { var p = path.split("\\").join("/").replace(localPath, ""); //windows fix if (p.charAt(p.length - 1) !== "/") { self.addFile(zipPath+p, fs.readFileSync(path), "", 0) } else { self.addFile(zipPath+p, new Buffer(0), "", 0) } }); } } else { throw Utils.Errors.FILE_NOT_FOUND.replace("%s", localPath); } }, /** * 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 entryName * @param content * @param comment * @param attr */ addFile : function(/*String*/entryName, /*Buffer*/content, /*String*/comment, /*Number*/attr) { var entry = new ZipEntry(); entry.entryName = entryName; entry.comment = comment || ""; entry.attr = attr || 438; //0666; if (entry.isDirectory && content.length) { // throw Utils.Errors.DIRECTORY_CONTENT_ERROR; } entry.setData(content); _zip.setEntry(entry); }, /** * Returns an array of ZipEntry objects representing the files and folders inside the archive * * @return Array */ getEntries : function() { if (_zip) { return _zip.entries; } else { return []; } }, /** * Returns a ZipEntry object representing the file or folder specified by ``name``. * * @param name * @return ZipEntry */ getEntry : function(/*String*/name) { return getEntry(name); }, /** * 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 entry ZipEntry object or String with the full path of the entry * @param targetPath Target folder where to write the file * @param maintainEntryPath 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 overwrite If the file already exists at the target path, the file will be overwriten if this is true. * Default is FALSE * * @return Boolean */ extractEntryTo : function(/*Object*/entry, /*String*/targetPath, /*Boolean*/maintainEntryPath, /*Boolean*/overwrite) { overwrite = overwrite || false; maintainEntryPath = typeof maintainEntryPath == "undefined" ? true : maintainEntryPath; var item = getEntry(entry); if (!item) { throw Utils.Errors.NO_ENTRY; } var target = pth.resolve(targetPath, maintainEntryPath ? item.entryName : pth.basename(item.entryName)); if (item.isDirectory) { target = pth.resolve(target, ".."); 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; } Utils.writeFileTo(pth.resolve(targetPath, maintainEntryPath ? child.entryName : child.entryName.substr(item.entryName.length)), content, overwrite); }); return true; } var content = item.getData(); if (!content) throw Utils.Errors.CANT_EXTRACT_FILE; if (fs.existsSync(targetPath) && !overwrite) { throw Utils.Errors.CANT_OVERRIDE; } Utils.writeFileTo(target, content, overwrite); return true; }, /** * Extracts the entire archive to the given location * * @param targetPath Target location * @param overwrite If the file already exists at the target path, the file will be overwriten if this is true. * Default is FALSE */ extractAllTo : function(/*String*/targetPath, /*Boolean*/overwrite) { overwrite = overwrite || false; if (!_zip) { throw Utils.Errors.NO_ZIP; } _zip.entries.forEach(function(entry) { if (entry.isDirectory) { Utils.makeDir(pth.resolve(targetPath, entry.entryName.toString())); return; } var content = entry.getData(); if (!content) { throw Utils.Errors.CANT_EXTRACT_FILE + "2"; } Utils.writeFileTo(pth.resolve(targetPath, entry.entryName.toString()), content, overwrite); }) }, /** * 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 targetFileName * @param callback */ writeZip : function(/*String*/targetFileName, /*Function*/callback) { if (arguments.length == 1) { if (typeof targetFileName == "function") { callback = targetFileName; targetFileName = ""; } } if (!targetFileName && _filename) { targetFileName = _filename; } if (!targetFileName) return; var zipData = _zip.compressToBuffer(); if (zipData) { Utils.writeFileTo(targetFileName, zipData, true); } }, /** * Returns the content of the entire zip file as a Buffer object * * @return Buffer */ toBuffer : function(/*Function*/onSuccess,/*Function*/onFail,/*Function*/onItemStart,/*Function*/onItemEnd) { this.valueOf = 2; if (typeof onSuccess == "function") { _zip.toAsyncBuffer(onSuccess,onFail,onItemStart,onItemEnd); return null; } return _zip.compressToBuffer() } } };