fix: canvas image/video download - fix data URL parsing in cvDownloadPreview (v1.5.21, hot 1.5.23)

This commit is contained in:
mac
2026-07-20 08:48:56 +08:00
parent 42e23663ee
commit aadd2b04ae
28 changed files with 5881 additions and 329 deletions
+98 -1
View File
@@ -47,4 +47,101 @@ function extractZip(buf, destDir) {
return written;
}
module.exports = { extractZip };
// ---- 零依赖 ZIP 压缩(deflate/stored),供员工团队打包等复用 ----
// CRC32 表(懒加载)
let CRC_TABLE = null;
function crc32(buf) {
if (!CRC_TABLE) {
CRC_TABLE = new Int32Array(256);
for (let n = 0; n < 256; n++) {
let c = n;
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
CRC_TABLE[n] = c;
}
}
let crc = ~0;
for (let i = 0; i < buf.length; i++) crc = (crc >>> 8) ^ CRC_TABLE[(crc ^ buf[i]) & 0xff];
return (~crc) >>> 0;
}
// MS-DOS 时间/日期(固定用一个合法值即可,不追求精确时间戳)
function dosDateTime(d) {
const date = d || new Date();
const time = ((date.getHours() & 0x1f) << 11) | ((date.getMinutes() & 0x3f) << 5) | ((date.getSeconds() / 2) & 0x1f);
const day = (((date.getFullYear() - 1980) & 0x7f) << 9) | (((date.getMonth() + 1) & 0x0f) << 5) | (date.getDate() & 0x1f);
return { time: time & 0xffff, date: day & 0xffff };
}
// files: [{ path, content(string|Buffer), mode?(unix 权限,如 0o755) }]
// 返回一个 zip 的 Buffer。目录靠文件路径里的 "/" 隐含,无需单独目录项。
function createZip(files) {
const { time, date } = dosDateTime();
const locals = [];
const centrals = [];
let offset = 0;
for (const f of files) {
const nameBuf = Buffer.from(String(f.path).replace(/^\/+/, ""), "utf8");
const raw = Buffer.isBuffer(f.content) ? f.content : Buffer.from(String(f.content == null ? "" : f.content), "utf8");
const crc = crc32(raw);
// 尝试 deflate;若压不小就存储(method 0)
let method = 8;
let comp = zlib.deflateRawSync(raw, { level: 9 });
if (comp.length >= raw.length) { method = 0; comp = raw; }
// 本地文件头
const lh = Buffer.alloc(30);
lh.writeUInt32LE(0x04034b50, 0);
lh.writeUInt16LE(20, 4); // version needed
lh.writeUInt16LE(0, 6); // flags
lh.writeUInt16LE(method, 8);
lh.writeUInt16LE(time, 10);
lh.writeUInt16LE(date, 12);
lh.writeUInt32LE(crc, 14);
lh.writeUInt32LE(comp.length, 18);
lh.writeUInt32LE(raw.length, 22);
lh.writeUInt16LE(nameBuf.length, 26);
lh.writeUInt16LE(0, 28); // extra len
locals.push(lh, nameBuf, comp);
// 中央目录项
const ch = Buffer.alloc(46);
// unix 权限写进外部属性高 16 位(低位保留),默认 0644,可执行给 0755
const unixMode = f.mode ? f.mode : 0o644;
const externalAttr = (unixMode << 16) >>> 0;
ch.writeUInt32LE(0x02014b50, 0);
ch.writeUInt16LE(0x031e, 4); // version made by (unix)
ch.writeUInt16LE(20, 6); // version needed
ch.writeUInt16LE(0, 8); // flags
ch.writeUInt16LE(method, 10);
ch.writeUInt16LE(time, 12);
ch.writeUInt16LE(date, 14);
ch.writeUInt32LE(crc, 16);
ch.writeUInt32LE(comp.length, 20);
ch.writeUInt32LE(raw.length, 24);
ch.writeUInt16LE(nameBuf.length, 28);
ch.writeUInt16LE(0, 30); // extra len
ch.writeUInt16LE(0, 32); // comment len
ch.writeUInt16LE(0, 34); // disk number
ch.writeUInt16LE(0, 36); // internal attr
ch.writeUInt32LE(externalAttr, 38);
ch.writeUInt32LE(offset, 42);
centrals.push(ch, nameBuf);
offset += lh.length + nameBuf.length + comp.length;
}
const localPart = Buffer.concat(locals);
const centralPart = Buffer.concat(centrals);
const eocd = Buffer.alloc(22);
eocd.writeUInt32LE(0x06054b50, 0);
eocd.writeUInt16LE(0, 4);
eocd.writeUInt16LE(0, 6);
eocd.writeUInt16LE(files.length, 8);
eocd.writeUInt16LE(files.length, 10);
eocd.writeUInt32LE(centralPart.length, 12);
eocd.writeUInt32LE(localPart.length, 16); // central dir offset
eocd.writeUInt16LE(0, 20);
return Buffer.concat([localPart, centralPart, eocd]);
}
module.exports = { extractZip, createZip };