Last active
December 11, 2024 15:29
-
-
Save rvaiya/4a2192df729056880a027789ae3cd4b7 to your computer and use it in GitHub Desktop.
tinyzip.js - A tiny zip file generator in 70 lines of javascript.
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
// Author: Raheman Vaiya | |
// License: WTFPL | |
// | |
// A tiny zip generator in < 70 lines of javascript. | |
// Produces an uncompressed zip blob when fed an array | |
// of the form: | |
// | |
// [{name: 'filename', data: <Uint8Array>}] | |
// | |
// Useful for bookmarklets, don't use this in production :P. | |
// | |
// Less than 2% the size of minified jszip (without all the bells and whistles). | |
// | |
// Refs: | |
// | |
// https://en.wikipedia.org/wiki/ZIP_(file_format) | |
// https://gist.github.com/rvaiya/9b39813d74ce3d5e1412e6b813a29c3b#file-zip-implementation-go | |
function zip(files) { | |
// Generate the crc32 table instead of hardcoding it to avoid having a giant constant | |
// in the minified output... | |
const crc32_table = function() { | |
const tbl = []; | |
var c; | |
for(var n = 0; n < 256; n++){ | |
c = n; | |
for(var k =0; k < 8; k++){ | |
c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); | |
} | |
tbl[n] = c; | |
} | |
return tbl; | |
}(); | |
function crc32(arr) { | |
var crc = -1; | |
for(var i=0; i<arr.length; i++) { | |
crc = (crc >>> 8) ^ crc32_table[(crc ^ arr[i]) & 0xFF]; | |
} | |
return (crc ^ (-1)) >>> 0; | |
} | |
function putUint32s(arr, offset, ...values) { | |
const dv = new DataView(arr.buffer); | |
values.forEach((v,i)=>dv.setUint32(offset+i*4, v, true)); | |
} | |
function putUint16s(arr, offset, ...values) { | |
const dv = new DataView(arr.buffer); | |
values.forEach((v,i)=>dv.setUint16(offset+i*2, v, true)); | |
} | |
const records = []; | |
const te = new TextEncoder('utf8'); | |
var offset = 0; | |
var cdSz = 0; | |
files.forEach(file => { | |
const fh = new Uint8Array(30+file.name.length); | |
const fname = te.encode(file.name); | |
const chksum = crc32(file.data); | |
putUint32s(fh, 0, 0x04034b50); | |
putUint32s(fh, 14, chksum, file.data.length, file.data.length); | |
putUint16s(fh, 26, file.name.length); | |
fh.set(fname, 30); | |
file.header = fh; | |
file.offset = offset; | |
records.push(fh); | |
records.push(file.data); | |
//Create CD records pending flush at the end... | |
file.cdr = new Uint8Array(46+file.name.length); | |
putUint32s(file.cdr, 0, 0x02014b50); | |
putUint32s(file.cdr, 16, chksum, file.data.length, file.data.length); | |
putUint16s(file.cdr, 28, file.name.length); | |
putUint32s(file.cdr, 42, offset); | |
file.cdr.set(fname, 46); | |
cdSz += file.cdr.length; | |
offset += file.header.length + file.data.length; | |
}); | |
//Push all accrued CD records.. | |
files.forEach(f=>records.push(f.cdr)); | |
//Add EOCD record... | |
const eocd = new Uint8Array(22); | |
putUint32s(eocd, 0, 0x06054b50); | |
putUint16s(eocd, 8, files.length, files.length); | |
putUint32s(eocd, 12, cdSz, offset); | |
records.push(eocd); | |
return new Blob(records, {type : 'application/zip'}); | |
} |
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
function zip(k){function f(a,b,...d){const c=new DataView(a.buffer);d.forEach((e,g)=>c.setUint32(b+4*g,e,!0))}function n(a,b,...d){const c=new DataView(a.buffer);d.forEach((e,g)=>c.setUint16(b+2*g,e,!0))}const q=function(){const a=[];for(var b,d=0;256>d;d++){b=d;for(var c=0;8>c;c++)b=b&1?3988292384^b>>>1:b>>>1;a[d]=b}return a}(),h=[],r=new TextEncoder("utf8");var l=0,p=0;k.forEach(a=>{const b=new Uint8Array(30+a.name.length),d=r.encode(a.name);var c=a.data;for(var e=-1,g=0;g<c.length;g++)e=e>>>8^ | |
q[(e^c[g])&255];c=(e^-1)>>>0;f(b,0,67324752);f(b,14,c,a.data.length,a.data.length);n(b,26,a.name.length);b.set(d,30);a.header=b;a.offset=l;h.push(b);h.push(a.data);a.cdr=new Uint8Array(46+a.name.length);f(a.cdr,0,33639248);f(a.cdr,16,c,a.data.length,a.data.length);n(a.cdr,28,a.name.length);f(a.cdr,42,l);a.cdr.set(d,46);p+=a.cdr.length;l+=a.header.length+a.data.length});k.forEach(a=>h.push(a.cdr));const m=new Uint8Array(22);f(m,0,101010256);n(m,8,k.length,k.length);f(m,12,p,l);h.push(m);return new Blob(h, | |
{type:"application/zip"})}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment