js-unzip.js (4891B)
1 (function (GLOBAL) { 2 function decodeUTF8(s){ 3 return decodeURIComponent(escape(s)); 4 } 5 6 var JSUnzip = function (fileContents) { 7 this.fileContents = new JSUnzip.BigEndianBinaryStream(fileContents); 8 } 9 GLOBAL.JSUnzip = JSUnzip; 10 JSUnzip.MAGIC_NUMBER = 0x04034b50; 11 12 JSUnzip.prototype = { 13 readEntries: function () { 14 if (!this.isZipFile()) { 15 throw new Error("File is not a Zip file."); 16 } 17 18 this.entries = []; 19 var e = new JSUnzip.ZipEntry(this.fileContents); 20 while (typeof(e.data) === "string") { 21 this.entries.push(e); 22 e = new JSUnzip.ZipEntry(this.fileContents); 23 } 24 }, 25 26 isZipFile: function () { 27 return this.fileContents.getByteRangeAsNumber(0, 4) === JSUnzip.MAGIC_NUMBER; 28 } 29 } 30 31 JSUnzip.ZipEntry = function (binaryStream) { 32 this.signature = binaryStream.getNextBytesAsNumber(4); 33 if (this.signature !== JSUnzip.MAGIC_NUMBER) { 34 return; 35 } 36 37 this.versionNeeded = binaryStream.getNextBytesAsNumber(2); 38 this.bitFlag = binaryStream.getNextBytesAsNumber(2); 39 this.compressionMethod = binaryStream.getNextBytesAsNumber(2); 40 this.timeBlob = binaryStream.getNextBytesAsNumber(4); 41 42 if (this.isEncrypted()) { 43 throw "File contains encrypted entry. Not supported."; 44 } 45 46 47 this.crc32 = binaryStream.getNextBytesAsNumber(4); 48 this.compressedSize = binaryStream.getNextBytesAsNumber(4); 49 this.uncompressedSize = binaryStream.getNextBytesAsNumber(4); 50 51 if (this.isUsingZip64()) { 52 throw "File is using Zip64 (4gb+ file size). Not supported"; 53 } 54 55 this.fileNameLength = binaryStream.getNextBytesAsNumber(2); 56 this.extraFieldLength = binaryStream.getNextBytesAsNumber(2); 57 58 this.fileName = binaryStream.getNextBytesAsString(this.fileNameLength); 59 this.extra = binaryStream.getNextBytesAsString(this.extraFieldLength); 60 this.data = binaryStream.getNextBytesAsString(this.compressedSize); 61 62 if (this.isUsingBit3TrailingDataDescriptor()) { 63 if (typeof(console) !== "undefined") { 64 console.log( "File is using bit 3 trailing data descriptor. Not supported."); 65 } 66 binaryStream.getNextBytesAsNumber(16); //Skip the descriptor and move to beginning of next ZipEntry 67 } 68 69 if (this.isUsingUtf8()) { 70 this.fileName = decodeUTF8(this.fileName); 71 } 72 73 } 74 75 JSUnzip.ZipEntry.prototype = { 76 isEncrypted: function () { 77 return (this.bitFlag & 0x01) === 0x01; 78 }, 79 80 isUsingUtf8: function () { 81 return (this.bitFlag & 0x0800) === 0x0800; 82 }, 83 84 isUsingBit3TrailingDataDescriptor: function () { 85 return (this.bitFlag & 0x0008) === 0x0008; 86 }, 87 88 isUsingZip64: function () { 89 return this.compressedSize === 0xFFFFFFFF || 90 this.uncompressedSize === 0xFFFFFFFF; 91 } 92 } 93 94 JSUnzip.BigEndianBinaryStream = function (stream) { 95 this.stream = stream; 96 this.resetByteIndex(); 97 } 98 99 JSUnzip.BigEndianBinaryStream.prototype = { 100 // The index of the current byte, used when we step through the byte 101 // with getNextBytesAs*. 102 resetByteIndex: function () { 103 this.currentByteIndex = 0; 104 }, 105 106 getByteAt: function (index) { 107 return this.stream.charCodeAt(index); 108 }, 109 110 getNextBytesAsNumber: function (steps) { 111 var res = this.getByteRangeAsNumber(this.currentByteIndex, steps); 112 this.currentByteIndex += steps; 113 return res; 114 }, 115 116 getNextBytesAsString: function (steps) { 117 var res = this.getByteRangeAsString(this.currentByteIndex, steps); 118 this.currentByteIndex += steps; 119 return res; 120 }, 121 122 // Big endian, so we're going backwards. 123 getByteRangeAsNumber: function (index, steps) { 124 var result = 0; 125 var i = index + steps - 1; 126 while (i >= index) { 127 result = (result << 8) + this.getByteAt(i); 128 i--; 129 } 130 return result; 131 }, 132 133 getByteRangeAsString: function (index, steps) { 134 var result = ""; 135 var max = index + steps; 136 var i = index; 137 while (i < max) { 138 var charCode = this.getByteAt(i); 139 result += String.fromCharCode(charCode); 140 // Accounting for multi-byte strings. 141 max -= Math.floor(charCode / 0x100); 142 i++; 143 } 144 return result; 145 } 146 } 147 }(this));