96 lines
3.2 KiB
JavaScript
96 lines
3.2 KiB
JavaScript
/**
|
|
* ZIP file creation utility for ComfyUI Viewer
|
|
* Creates ZIP blobs without external dependencies
|
|
*/
|
|
|
|
/**
|
|
* Create a ZIP blob from an array of files
|
|
* @param {Array<{name: string, content: string}>} files - Files to include
|
|
* @returns {Promise<Blob>} ZIP blob
|
|
*/
|
|
export async function createZipBlob(files) {
|
|
const crc32 = (data) => {
|
|
let crc = 0xFFFFFFFF;
|
|
const table = new Uint32Array(256);
|
|
for (let i = 0; i < 256; i++) {
|
|
let c = i;
|
|
for (let j = 0; j < 8; j++) c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
|
|
table[i] = c;
|
|
}
|
|
for (let i = 0; i < data.length; i++) crc = table[(crc ^ data[i]) & 0xFF] ^ (crc >>> 8);
|
|
return (crc ^ 0xFFFFFFFF) >>> 0;
|
|
};
|
|
|
|
const encoder = new TextEncoder();
|
|
const parts = [];
|
|
const centralDir = [];
|
|
let offset = 0;
|
|
|
|
for (const file of files) {
|
|
const nameBytes = encoder.encode(file.name);
|
|
const contentBytes = encoder.encode(file.content);
|
|
const crc = crc32(contentBytes);
|
|
const localHeader = new Uint8Array(30 + nameBytes.length);
|
|
const view = new DataView(localHeader.buffer);
|
|
|
|
view.setUint32(0, 0x04034B50, true);
|
|
view.setUint16(4, 20, true);
|
|
view.setUint16(6, 0, true);
|
|
view.setUint16(8, 0, true);
|
|
view.setUint16(10, 0, true);
|
|
view.setUint16(12, 0, true);
|
|
view.setUint32(14, crc, true);
|
|
view.setUint32(18, contentBytes.length, true);
|
|
view.setUint32(22, contentBytes.length, true);
|
|
view.setUint16(26, nameBytes.length, true);
|
|
view.setUint16(28, 0, true);
|
|
localHeader.set(nameBytes, 30);
|
|
|
|
const centralEntry = new Uint8Array(46 + nameBytes.length);
|
|
const cView = new DataView(centralEntry.buffer);
|
|
cView.setUint32(0, 0x02014B50, true);
|
|
cView.setUint16(4, 20, true);
|
|
cView.setUint16(6, 20, true);
|
|
cView.setUint16(8, 0, true);
|
|
cView.setUint16(10, 0, true);
|
|
cView.setUint16(12, 0, true);
|
|
cView.setUint16(14, 0, true);
|
|
cView.setUint32(16, crc, true);
|
|
cView.setUint32(20, contentBytes.length, true);
|
|
cView.setUint32(24, contentBytes.length, true);
|
|
cView.setUint16(28, nameBytes.length, true);
|
|
cView.setUint16(30, 0, true);
|
|
cView.setUint16(32, 0, true);
|
|
cView.setUint16(34, 0, true);
|
|
cView.setUint16(36, 0, true);
|
|
cView.setUint32(38, 0, true);
|
|
cView.setUint32(42, offset, true);
|
|
centralEntry.set(nameBytes, 46);
|
|
|
|
parts.push(localHeader, contentBytes);
|
|
centralDir.push(centralEntry);
|
|
offset += localHeader.length + contentBytes.length;
|
|
}
|
|
|
|
const centralDirOffset = offset;
|
|
let centralDirSize = 0;
|
|
for (const entry of centralDir) {
|
|
parts.push(entry);
|
|
centralDirSize += entry.length;
|
|
}
|
|
|
|
const endRecord = new Uint8Array(22);
|
|
const endView = new DataView(endRecord.buffer);
|
|
endView.setUint32(0, 0x06054B50, true);
|
|
endView.setUint16(4, 0, true);
|
|
endView.setUint16(6, 0, true);
|
|
endView.setUint16(8, files.length, true);
|
|
endView.setUint16(10, files.length, true);
|
|
endView.setUint32(12, centralDirSize, true);
|
|
endView.setUint32(16, centralDirOffset, true);
|
|
endView.setUint16(20, 0, true);
|
|
parts.push(endRecord);
|
|
|
|
return new Blob(parts, { type: "application/zip" });
|
|
}
|