2 var StringReader = require('./stringReader');
3 var NodeBufferReader = require('./nodeBufferReader');
4 var Uint8ArrayReader = require('./uint8ArrayReader');
5 var utils = require('./utils');
6 var sig = require('./signature');
7 var ZipEntry = require('./zipEntry');
8 var support = require('./support');
9 var jszipProto = require('./object');
10 // class ZipEntries {{{
12 * All the entries in the zip file.
14 * @param {String|ArrayBuffer|Uint8Array} data the binary stream to load.
15 * @param {Object} loadOptions Options for loading the stream.
17 function ZipEntries(data, loadOptions) {
19 this.loadOptions = loadOptions;
24 ZipEntries.prototype = {
26 * Check that the reader is on the speficied signature.
27 * @param {string} expectedSignature the expected signature.
28 * @throws {Error} if it is an other signature.
30 checkSignature: function(expectedSignature) {
31 var signature = this.reader.readString(4);
32 if (signature !== expectedSignature) {
33 throw new Error("Corrupted zip or bug : unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")");
37 * Read the end of the central directory.
39 readBlockEndOfCentral: function() {
40 this.diskNumber = this.reader.readInt(2);
41 this.diskWithCentralDirStart = this.reader.readInt(2);
42 this.centralDirRecordsOnThisDisk = this.reader.readInt(2);
43 this.centralDirRecords = this.reader.readInt(2);
44 this.centralDirSize = this.reader.readInt(4);
45 this.centralDirOffset = this.reader.readInt(4);
47 this.zipCommentLength = this.reader.readInt(2);
48 // warning : the encoding depends of the system locale
49 // On a linux machine with LANG=en_US.utf8, this field is utf8 encoded.
50 // On a windows machine, this field is encoded with the localized windows code page.
51 this.zipComment = this.reader.readString(this.zipCommentLength);
52 // To get consistent behavior with the generation part, we will assume that
53 // this is utf8 encoded.
54 this.zipComment = jszipProto.utf8decode(this.zipComment);
57 * Read the end of the Zip 64 central directory.
58 * Not merged with the method readEndOfCentral :
59 * The end of central can coexist with its Zip64 brother,
60 * I don't want to read the wrong number of bytes !
62 readBlockZip64EndOfCentral: function() {
63 this.zip64EndOfCentralSize = this.reader.readInt(8);
64 this.versionMadeBy = this.reader.readString(2);
65 this.versionNeeded = this.reader.readInt(2);
66 this.diskNumber = this.reader.readInt(4);
67 this.diskWithCentralDirStart = this.reader.readInt(4);
68 this.centralDirRecordsOnThisDisk = this.reader.readInt(8);
69 this.centralDirRecords = this.reader.readInt(8);
70 this.centralDirSize = this.reader.readInt(8);
71 this.centralDirOffset = this.reader.readInt(8);
73 this.zip64ExtensibleData = {};
74 var extraDataSize = this.zip64EndOfCentralSize - 44,
79 while (index < extraDataSize) {
80 extraFieldId = this.reader.readInt(2);
81 extraFieldLength = this.reader.readInt(4);
82 extraFieldValue = this.reader.readString(extraFieldLength);
83 this.zip64ExtensibleData[extraFieldId] = {
85 length: extraFieldLength,
86 value: extraFieldValue
91 * Read the end of the Zip 64 central directory locator.
93 readBlockZip64EndOfCentralLocator: function() {
94 this.diskWithZip64CentralDirStart = this.reader.readInt(4);
95 this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8);
96 this.disksCount = this.reader.readInt(4);
97 if (this.disksCount > 1) {
98 throw new Error("Multi-volumes zip are not supported");
102 * Read the local files, based on the offset read in the central part.
104 readLocalFiles: function() {
106 for (i = 0; i < this.files.length; i++) {
107 file = this.files[i];
108 this.reader.setIndex(file.localHeaderOffset);
109 this.checkSignature(sig.LOCAL_FILE_HEADER);
110 file.readLocalPart(this.reader);
112 file.processAttributes();
116 * Read the central directory.
118 readCentralDir: function() {
121 this.reader.setIndex(this.centralDirOffset);
122 while (this.reader.readString(4) === sig.CENTRAL_FILE_HEADER) {
123 file = new ZipEntry({
125 }, this.loadOptions);
126 file.readCentralPart(this.reader);
127 this.files.push(file);
131 * Read the end of central directory.
133 readEndOfCentral: function() {
134 var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END);
136 // Check if the content is a truncated zip or complete garbage.
137 // A "LOCAL_FILE_HEADER" is not required at the beginning (auto
138 // extractible zip for example) but it can give a good hint.
139 // If an ajax request was used without responseType, we will also
140 // get unreadable data.
141 var isGarbage = true;
143 this.reader.setIndex(0);
144 this.checkSignature(sig.LOCAL_FILE_HEADER);
149 throw new Error("Can't find end of central directory : is this a zip file ? " +
150 "If it is, see http://stuk.github.io/jszip/documentation/howto/read_zip.html");
152 throw new Error("Corrupted zip : can't find end of central directory");
155 this.reader.setIndex(offset);
156 this.checkSignature(sig.CENTRAL_DIRECTORY_END);
157 this.readBlockEndOfCentral();
160 /* extract from the zip spec :
161 4) If one of the fields in the end of central directory
162 record is too small to hold required data, the field
163 should be set to -1 (0xFFFF or 0xFFFFFFFF) and the
164 ZIP64 format record should be created.
165 5) The end of central directory record and the
166 Zip64 end of central directory locator record must
167 reside on the same disk when splitting or spanning
170 if (this.diskNumber === utils.MAX_VALUE_16BITS || this.diskWithCentralDirStart === utils.MAX_VALUE_16BITS || this.centralDirRecordsOnThisDisk === utils.MAX_VALUE_16BITS || this.centralDirRecords === utils.MAX_VALUE_16BITS || this.centralDirSize === utils.MAX_VALUE_32BITS || this.centralDirOffset === utils.MAX_VALUE_32BITS) {
174 Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from
175 the zip file can fit into a 32bits integer. This cannot be solved : Javascript represents
176 all numbers as 64-bit double precision IEEE 754 floating point numbers.
177 So, we have 53bits for integers and bitwise operations treat everything as 32bits.
178 see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators
179 and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5
182 // should look for a zip64 EOCD locator
183 offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
185 throw new Error("Corrupted zip : can't find the ZIP64 end of central directory locator");
187 this.reader.setIndex(offset);
188 this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
189 this.readBlockZip64EndOfCentralLocator();
191 // now the zip64 EOCD record
192 this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir);
193 this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END);
194 this.readBlockZip64EndOfCentral();
197 prepareReader: function(data) {
198 var type = utils.getTypeOf(data);
199 if (type === "string" && !support.uint8array) {
200 this.reader = new StringReader(data, this.loadOptions.optimizedBinaryString);
202 else if (type === "nodebuffer") {
203 this.reader = new NodeBufferReader(data);
206 this.reader = new Uint8ArrayReader(utils.transformTo("uint8array", data));
210 * Read a zip file and create ZipEntries.
211 * @param {String|ArrayBuffer|Uint8Array|Buffer} data the binary string representing a zip file.
213 load: function(data) {
214 this.prepareReader(data);
215 this.readEndOfCentral();
216 this.readCentralDir();
217 this.readLocalFiles();
220 // }}} end of ZipEntries
221 module.exports = ZipEntries;