Files

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" });
}