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() | |
} | |
} | |
}; |