1 jls.loader.provide('jls.util.zip.ZipFile'); 2 3 jls.loader.require('jls.util.zip.ZipEntry'); 4 jls.loader.require('jls.util.zip.Inflater'); 5 jls.loader.require('jls.io.File'); 6 jls.loader.require('jls.io.FileChannel'); 7 jls.loader.require('jls.lang.Struct'); 8 9 jls.util.zip.ZipFile = jls.lang.Class.create( /** @lends jls.util.zip.ZipFile.prototype */ 10 { 11 /** 12 * Creates a zip file. 13 * 14 * @param {jls.io.File} file The file . 15 * @constructs 16 * @class This class represents a zip file. 17 */ 18 initialize : function(file) { 19 this._buffer = jls.lang.ByteBuffer.allocate(1024); 20 var buffer = jls.lang.ByteBuffer.allocate(64); 21 buffer.setByteOrder(jls.lang.Buffer.LITTLE_ENDIAN); 22 23 this._endOfCentralDirectoryRecord = new jls.lang.Struct(jls.util.zip.ZipEntry.getEndOfCentralDirectoryRecordStructDefinition(), buffer); // 22 24 this._dataDescriptor = new jls.lang.Struct(jls.util.zip.ZipEntry.getDataDescriptorStructDefinition(), buffer); // 12 25 this._localFileHeader = new jls.lang.Struct(jls.util.zip.ZipEntry.getLocalFileHeaderStructDefinition(), buffer); // 30 26 this._fileHeader = new jls.lang.Struct(jls.util.zip.ZipEntry.getFileHeaderStructDefinition(), buffer); // 46 27 28 this._file = file; 29 this._channel = new jls.io.FileChannel(this._file); 30 this._entryCount = 0; 31 this._entries = {}; 32 33 try { 34 //this._channel.seek(this._file.size() - this._endOfCentralDirectoryRecord.size()); 35 this._channel.seek(-this._endOfCentralDirectoryRecord.size(), jls.io.FileDescriptor.SEEK_END); 36 37 this._readStruct(this._endOfCentralDirectoryRecord, jls.util.zip.ZipEntry.END_CENTRAL_DIR_SIGNATURE, 'End Of Central Directory Record'); 38 this._entryCount = this._endOfCentralDirectoryRecord.get('entryCount'); 39 jls.logger.debug('entry count: ' + this._entryCount); 40 41 this._channel.seek(this._endOfCentralDirectoryRecord.get('offset')); 42 for (var i = 0; i < this._entryCount; i++) { 43 this._readStruct(this._fileHeader, jls.util.zip.ZipEntry.FILE_HEADER_SIGNATURE, 'File Header'); 44 var filename = this._readString(this._fileHeader.get('filenameLength')); 45 this._channel.seek(this._fileHeader.get('extraFieldLength'), jls.io.FileDescriptor.SEEK_CUR); 46 var comment = this._readString(this._fileHeader.get('fileCommentLength')); 47 jls.logger.debug('entry: "' + filename + '"'); 48 this._entries[filename] = new jls.util.zip.ZipEntry(filename, comment, null, this._fileHeader); 49 } 50 } 51 catch(e) { 52 this.close(); 53 throw e; 54 } 55 }, 56 _readString : function(length) { 57 this._buffer.clear(); 58 this._buffer.setLimit(length); 59 this._channel.read(this._buffer); 60 if (this._buffer.remaining() != 0) { 61 throw new jls.lang.Exception('Unable to read string'); 62 } 63 this._buffer.flip(); 64 return this._buffer.getString('UTF-8'); 65 }, 66 _readStruct : function(str, signature, name) { 67 name = name || 'structure'; 68 str.clear(); 69 this._channel.read(str.buffer()); 70 if (str.buffer().remaining() != 0) { 71 throw new jls.lang.Exception('Unable to read ' + name); 72 } 73 var sig = str.get('signature'); 74 jls.logger.debug('signature: 0x' + sig.toString(16)); 75 if (sig != signature) { 76 throw new jls.lang.Exception('Invalid ' + name + ' signature (0x' + sig.toString(16) + ' != 0x' + signature.toString(16) + ')'); 77 } 78 }, 79 /** 80 * Closes the zip file. 81 * 82 */ 83 close : function() { 84 this._channel.close(); 85 this._entryCount = 0; 86 this._entries = {}; 87 }, 88 /** 89 * Returns the zip entry count. 90 * 91 * @returns {Number} The zip entry count. 92 */ 93 size : function() { 94 return this._entryCount; 95 }, 96 /** 97 * Returns an entry depending on the specified name. 98 * 99 * @param {String} name The entry name. 100 * @returns {jls.util.zip.ZipEntry} The zip entry or null. 101 */ 102 getEntry : function(name) { 103 if (name in this._entries) { 104 return this._entries[name]; 105 } 106 return null; 107 }, 108 /** 109 * Tells if this zip file contains a specified entry name. 110 * 111 * @param {String} name The entry name. 112 * @returns {Boolean} true if this zip file contains a specified entry name. 113 */ 114 hasEntry : function(name) { 115 return name in this._entries; 116 }, 117 /** 118 * Returns a byte buffer containing the uncompressed entry content. 119 * 120 * @param {String} name The entry name. 121 * @returns {jls.lang.ByteBuffer} The uncompressed entry content. 122 */ 123 getEntryContent : function(name) { 124 var entry = this.getEntry(name); 125 if (entry == null) { 126 throw new jls.lang.Exception('Entry not found'); 127 } 128 if (entry.getMethod() != jls.util.zip.ZipEntry.COMPRESSION_METHOD_DEFLATED) { 129 throw new jls.lang.Exception('Invalid method (' + entry.getMethod() + ')'); 130 } 131 this._channel.seek(entry.getOffset(), jls.io.FileDescriptor.SEEK_SET); 132 this._readStruct(this._localFileHeader, jls.util.zip.ZipEntry.LOCAL_FILE_HEADER_SIGNATURE, 'Local File Header'); 133 this._channel.seek(this._localFileHeader.get('filenameLength') + this._localFileHeader.get('extraFieldLength'), jls.io.FileDescriptor.SEEK_CUR); 134 var compressedSize = this._localFileHeader.get('compressedSize'); 135 var uncompressedSize = this._localFileHeader.get('uncompressedSize'); 136 jls.logger.debug('compressedSize: ' + compressedSize); 137 jls.logger.debug('uncompressedSize: ' + uncompressedSize); 138 var inputBuffer = jls.lang.ByteBuffer.allocate(compressedSize); 139 var outputBuffer = jls.lang.ByteBuffer.allocate(uncompressedSize); 140 141 var inflater = new jls.util.zip.Inflater(); 142 this._channel.read(inputBuffer); 143 inputBuffer.flip(); 144 inflater.setInput(inputBuffer); 145 inflater.inflate(outputBuffer); 146 inflater.end(); 147 148 outputBuffer.flip(); 149 inputBuffer.free(); 150 return outputBuffer; 151 }, 152 getEntryContentAsString : function(name, csn) { 153 var content = this.getEntryContent(name); 154 return content.getString((typeof csn != 'undefined') ? csn : 'UTF-8'); 155 }, 156 getInputStream : function(entry) { 157 return null; 158 }, 159 /** 160 * Returns the zip entries. 161 * 162 * @returns {Array} The zip entries. 163 */ 164 entries : function() { 165 var list = []; 166 for (var name in this._entries) { 167 list.push(this._entries[name]) 168 } 169 return list; 170 }, 171 /** 172 * Returns the file name. 173 * 174 * @returns {String} The file name. 175 */ 176 getName : function() { 177 return this._file.getName(); 178 } 179 }); 180 181