-
-
Save nutbread/6d3f939d9295e95f135e to your computer and use it in GitHub Desktop.
Javascript to put data into a storage zip file (no compression)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Test | |
| var zw = new Zipper(), | |
| fi, url; | |
| zw.set_comment("あ"); | |
| fi = zw.add_file(zw.string_to_array("test1あ"), "test1あ.txt"); | |
| zw.set_file_comment(fi, "あ"); | |
| zw.set_file_extra(fi, "あ"); | |
| fi = zw.add_file(zw.string_to_array("test2あ"), "test2あ.txt"); | |
| zw.set_file_comment(fi, "2あ"); | |
| zw.set_file_extra(fi, "2あ"); | |
| zw.set_file_date(fi, new Date(1981, 10, 10)); | |
| zw.set_file_data(fi, "test3あ"); | |
| zw.set_file_name(fi, "test3あ.txt"); | |
| url = URL.createObjectURL(zw.to_blob()); | |
| document.body.innerHTML = '<a href="' + url + '" target=_blank download="test.zip">test</a>'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| var Zipper = (function () { | |
| var Zipper = function () { | |
| this.files = []; | |
| this.comment = ""; | |
| this.comment_data = string_to_array(this.comment, max_lengths.comment); | |
| this.date = new Date(); | |
| }; | |
| var max_lengths = { | |
| data: 0XFFFFFFFF, | |
| name: 0xFFFF, | |
| extra: 0xFFFF, | |
| comment: 0xFFFF, | |
| }; | |
| var signatures = { | |
| file: new Uint8Array([ 0x50 , 0x4B , 0x03 , 0x04 ]), | |
| cd: new Uint8Array([ 0x50 , 0x4B , 0x01 , 0x02 ]), | |
| footer: new Uint8Array([ 0x50 , 0x4B , 0x05 , 0x06 ]), | |
| }; | |
| var data_sizes = { | |
| file: 26, // 30 - 4 | |
| cd: 42, // 46 - 4 | |
| footer: 18, // 22 - 4 | |
| }; | |
| var ZIP_FLAG_UTF8 = 1 << 11; | |
| var typeof_str = typeof(""); | |
| var FileData = function (data, filename, extra, comment, date) { | |
| // Setup | |
| this.set_data(data); | |
| this.set_name(filename); | |
| this.set_extra(extra); | |
| this.set_comment(comment); | |
| this.set_date(date); | |
| // Other stuff | |
| this.offset = 0; | |
| this.crc = null; | |
| this.utf8 = true; | |
| }; | |
| FileData.prototype = { | |
| constructor: FileData, | |
| prepare: function (offset) { | |
| this.offset = offset; | |
| this.utf8 = ( | |
| this.name.length != this.name_data.length || | |
| this.extra.length != this.extra_data.length || | |
| this.comment.length != this.comment_data.length | |
| ); | |
| if (this.crc === null) { | |
| this.crc = crc32(this.data); | |
| } | |
| }, | |
| set_data: function (data) { | |
| if (typeof(data) == typeof_str) { | |
| // Convert to array | |
| this.data = string_to_array(data, max_lengths.data); | |
| } | |
| else { | |
| // Set and truncate if necessary | |
| this.data = data; | |
| if (this.data.length > max_lengths.data) { | |
| this.data = this.data.subarray(0, max_lengths.data); | |
| } | |
| } | |
| this.crc = null; | |
| }, | |
| set_name: function (filename) { | |
| this.name = filename; | |
| this.name_data = string_to_array(this.name, max_lengths.name); | |
| }, | |
| set_extra: function (extra) { | |
| this.extra = extra; | |
| this.extra_data = string_to_array(this.extra, max_lengths.extra); | |
| }, | |
| set_comment: function (comment) { | |
| this.comment = comment; | |
| this.comment_data = string_to_array(this.comment, max_lengths.comment); | |
| }, | |
| set_date: function (date) { | |
| this.date = date; | |
| this.date_zip = date_to_ziptime(this.date); | |
| }, | |
| }; | |
| var BufferWriter = function (buffer) { | |
| this.buffer = buffer; | |
| this.pos = 0; | |
| }; | |
| BufferWriter.prototype = { | |
| constructor: BufferWriter, | |
| reset: function (buffer) { | |
| this.buffer = buffer; | |
| this.pos = 0; | |
| }, | |
| write_ushort: function (value) { | |
| // Bound | |
| value = (value & 0x0000FFFF) >>> 0; | |
| // Write | |
| for (var i = 0; i < 2; ++i) { | |
| this.buffer[this.pos++] = value & 0xFF; | |
| value = value >>> 8; | |
| } | |
| }, | |
| write_uint: function (value) { | |
| // Bound | |
| value = (value & 0xFFFFFFFF) >>> 0; | |
| // Write | |
| for (var i = 0; i < 4; ++i) { | |
| this.buffer[this.pos++] = value & 0xFF; | |
| value = value >>> 8; | |
| } | |
| }, | |
| write_data: function (data) { | |
| // Write | |
| for (var i = 0, len = data.length; i < len; ++i) { | |
| this.buffer[this.pos++] = data[i]; | |
| } | |
| }, | |
| write_string: function (str) { | |
| // Write | |
| for (var i = 0, len = str.length; i < len; ++i) { | |
| this.buffer[this.pos++] = str.charCodeAt(i); | |
| } | |
| }, | |
| }; | |
| var crc32 = (function () { | |
| var crc_table = new Uint32Array([ //{ | |
| 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, | |
| 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, | |
| 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, | |
| 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, | |
| 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, | |
| 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, | |
| 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, | |
| 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, | |
| 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, | |
| 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, | |
| 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, | |
| 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, | |
| 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, | |
| 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, | |
| 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, | |
| 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, | |
| 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, | |
| 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, | |
| 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, | |
| 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, | |
| 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, | |
| 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, | |
| 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, | |
| 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, | |
| 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, | |
| 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, | |
| 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, | |
| 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, | |
| 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, | |
| 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, | |
| 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, | |
| 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, | |
| ]); //} | |
| return function (data) { | |
| var crc = (0 ^ (-1)), | |
| data_len = data.length, | |
| ct = crc_table, | |
| i; | |
| for (i = 0; i < data_len; ++i) { | |
| crc = (crc >>> 8) ^ ct[(crc ^ data[i]) & 0xFF]; | |
| } | |
| return (crc ^ (-1)) >>> 0; | |
| }; | |
| })(); | |
| var date_to_ziptime = function (date) { | |
| return [ | |
| (date.getDate()) | ((date.getMonth() + 1) << 5) | ((date.getFullYear() - 1980) << 9), // Date | |
| Math.floor(date.getSeconds() / 2) | (date.getMinutes() << 5) | (date.getHours() << 11) // Time | |
| ]; | |
| }; | |
| var array_to_string = function (arr) { | |
| // Copy | |
| var str = "", | |
| len = arr.length, | |
| chunk_size = 1024, | |
| i; | |
| for (i = 0; i < len; i += chunk_size) { | |
| str += String.fromCharCode.apply(null, arr.subarray(i, i + chunk_size)); | |
| } | |
| // Decode uft-8 | |
| return decodeURIComponent(escape(str)); | |
| }; | |
| var string_to_array = function (str, max_length) { | |
| // Encode to uft-8 | |
| str = unescape(encodeURIComponent(str)); | |
| // Copy | |
| var len = str.length, | |
| buffer, i; | |
| if (max_length !== undefined && len > max_length) len = max_length; | |
| buffer = new Uint8Array(len); | |
| for (i = 0; i < len; ++i) { | |
| buffer[i] = str.charCodeAt(i); | |
| } | |
| // Done | |
| return buffer; | |
| }; | |
| var write_file_header = function (writer, file_data) { | |
| // Write the file header | |
| writer.write_ushort(20); // Version | |
| writer.write_ushort(file_data.utf8 ? ZIP_FLAG_UTF8 : 0); // Flags | |
| writer.write_ushort(0); // Compression (store) | |
| writer.write_ushort(file_data.date_zip[1]); // Modified time | |
| writer.write_ushort(file_data.date_zip[0]); // Modified date | |
| writer.write_uint(file_data.crc); // CRC32 | |
| writer.write_uint(file_data.data.length); // Compressed size | |
| writer.write_uint(file_data.data.length); // Unompressed size | |
| writer.write_ushort(file_data.name_data.length); // Filename length | |
| writer.write_ushort(file_data.extra_data.length); // Extra length | |
| }; | |
| var write_central_directory_header = function (writer, file_data) { | |
| // Write the central directory header | |
| writer.write_ushort(20); // Version | |
| writer.write_ushort(20); // Version required | |
| writer.write_ushort(file_data.utf8 ? ZIP_FLAG_UTF8 : 0); // Flags | |
| writer.write_ushort(0); // Compression (store) | |
| writer.write_ushort(file_data.date_zip[1]); // Modified time | |
| writer.write_ushort(file_data.date_zip[0]); // Modified date | |
| writer.write_uint(file_data.crc); // CRC32 | |
| writer.write_uint(file_data.data.length); // Compressed size | |
| writer.write_uint(file_data.data.length); // Unompressed size | |
| writer.write_ushort(file_data.name_data.length); // Filename length | |
| writer.write_ushort(file_data.extra_data.length); // Extra length | |
| writer.write_ushort(file_data.comment_data.length); // Comment length | |
| writer.write_ushort(0); // Disk number start | |
| writer.write_ushort(0); // Internal attr | |
| writer.write_uint(0); // External attr | |
| writer.write_uint(file_data.offset); // Offset | |
| }; | |
| var write_footer = function (writer, file_count, pos_cd, pos_footer) { | |
| // Write file footer | |
| writer.write_ushort(0); // Disk number | |
| writer.write_ushort(0); // Disk number with central directory | |
| writer.write_ushort(file_count); // Disk entries | |
| writer.write_ushort(file_count); // Total entries | |
| writer.write_uint(pos_footer - pos_cd); // Central directory size | |
| writer.write_uint(pos_cd); // Central directory start | |
| writer.write_ushort(this.comment_data.length); // Comment length | |
| }; | |
| Zipper.array_to_string = array_to_string; | |
| Zipper.string_to_array = string_to_array; | |
| Zipper.prototype = { | |
| constructor: Zipper, | |
| calculate_size: function () { | |
| var total_size = 0, | |
| file_count = this.files.length, | |
| i, f; | |
| // Calculate size | |
| for (i = 0; i < file_count; ++i) { | |
| f = this.files[i]; | |
| total_size += signatures.file.length + data_sizes.file + f.name_data.length + f.extra_data.length + f.data.length; // Entry | |
| total_size += signatures.cd.length + data_sizes.cd + f.name_data.length + f.extra_data.length + f.comment_data.length; // Central directory | |
| } | |
| // Footer length | |
| total_size += signatures.footer.length + data_sizes.footer + this.comment_data.length; | |
| // Done | |
| return total_size; | |
| }, | |
| set_date: function (date) { | |
| this.date = date; | |
| }, | |
| get_date: function () { | |
| return this.date; | |
| }, | |
| set_comment: function (str) { | |
| this.comment = str; | |
| this.comment_data = string_to_array(this.comment, max_lengths.comment); | |
| }, | |
| get_comment: function () { | |
| return this.comment; | |
| }, | |
| add_file: function (data, filename) { | |
| // Create | |
| var file_data = new FileData(data, filename, "", "", this.date); | |
| // Add | |
| this.files.push(file_data); | |
| // Done | |
| return this.files.length - 1; | |
| }, | |
| remove_file: function (index) { | |
| this.files.splice(index, 1); | |
| }, | |
| get_file_count: function () { | |
| return this.files.length; | |
| }, | |
| get_file_data: function (index) { | |
| return this.files[index].data; | |
| }, | |
| set_file_data: function (index, data) { | |
| this.files[index].set_data(data); | |
| }, | |
| get_file_name: function (index) { | |
| return this.files[index].name; | |
| }, | |
| set_file_name: function (index, name) { | |
| this.files[index].set_name(name); | |
| }, | |
| get_file_extra: function (index) { | |
| return this.files[index].extra; | |
| }, | |
| set_file_extra: function (index, extra) { | |
| this.files[index].set_extra(extra); | |
| }, | |
| get_file_comment: function (index) { | |
| return this.files[index].comment; | |
| }, | |
| set_file_comment: function (index, comment) { | |
| this.files[index].set_comment(comment); | |
| }, | |
| get_file_date: function (index) { | |
| return this.files[index].date; | |
| }, | |
| set_file_date: function (index, date) { | |
| this.files[index].set_date(date); | |
| }, | |
| to_buffer: function () { | |
| var total_size = this.calculate_size(), | |
| file_count = this.files.length, | |
| bw = new BufferWriter(new Uint8Array(total_size)), | |
| pos_cd, pos_footer, i, f; | |
| // Write file data | |
| for (i = 0; i < file_count; ++i) { | |
| f = this.files[i]; | |
| f.prepare(bw.pos); | |
| // Header | |
| bw.write_data(signatures.file); // Signature | |
| write_file_header.call(this, bw, f); | |
| // Data | |
| bw.write_data(f.name_data); // Name | |
| bw.write_data(f.extra_data); // Extra | |
| bw.write_data(f.data); // Data | |
| } | |
| // Central directory | |
| pos_cd = bw.pos; | |
| for (i = 0; i < file_count; ++i) { | |
| f = this.files[i]; | |
| // Entry | |
| bw.write_data(signatures.cd); // Signature | |
| write_central_directory_header.call(this, bw, f); | |
| // Data | |
| bw.write_data(f.name_data); // Name | |
| bw.write_data(f.extra_data); // Extra | |
| bw.write_data(f.comment_data); // Comment | |
| } | |
| // Footer | |
| pos_footer = bw.pos; | |
| bw.write_data(signatures.footer); // Signature | |
| write_footer.call(this, bw, file_count, pos_cd, pos_footer); | |
| bw.write_data(this.comment_data); // Comment | |
| // Done | |
| return bw.buffer; | |
| }, | |
| to_blob: function () { | |
| var file_count = this.files.length, | |
| data_array = [], | |
| bw = new BufferWriter(), | |
| pos = 0, | |
| pos_cd, i, f; | |
| // Write file data | |
| for (i = 0; i < file_count; ++i) { | |
| f = this.files[i]; | |
| f.prepare(pos); | |
| // Header | |
| bw.reset(new Uint8Array(data_sizes.file)); | |
| write_file_header.call(this, bw, f); | |
| data_array.push(signatures.file); // Signature | |
| data_array.push(bw.buffer); | |
| // Data | |
| data_array.push(f.name_data); // Name | |
| data_array.push(f.extra_data); // Extra | |
| data_array.push(f.data); // Data | |
| // Pos update | |
| pos += signatures.file.length + bw.buffer.length + f.name_data.length + f.extra_data.length + f.data.length; | |
| } | |
| // Central directory | |
| pos_cd = pos; | |
| for (i = 0; i < file_count; ++i) { | |
| f = this.files[i]; | |
| // Header | |
| bw.reset(new Uint8Array(data_sizes.cd)); | |
| write_central_directory_header.call(this, bw, f); | |
| data_array.push(signatures.cd); // Signature | |
| data_array.push(bw.buffer); | |
| // Data | |
| data_array.push(f.name_data); // Name | |
| data_array.push(f.extra_data); // Extra | |
| data_array.push(f.comment_data); // Comment | |
| // Pos update | |
| pos += signatures.cd.length + bw.buffer.length + f.name_data.length + f.extra_data.length + f.comment_data.length; | |
| } | |
| // Footer | |
| bw.reset(new Uint8Array(data_sizes.footer)); | |
| write_footer.call(this, bw, file_count, pos_cd, pos); | |
| data_array.push(signatures.footer); // Signature | |
| data_array.push(bw.buffer); | |
| data_array.push(this.comment_data); // Comment | |
| // Create as a blob | |
| return new Blob(data_array, { type: "application/zip" }); | |
| }, | |
| }; | |
| return Zipper; | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment