- копирую модуль upload/ из drhider (слои 1-2) - blueprint: параметр sink (drhider-сессия по умолчанию, сверка — DB+парсинг) - upload_bp: contracts_upload_sink = _store_and_parse - routes: регистрирую create_upload_refs_blueprint(cfg, sink=...) - app.py: корень репо в sys.path (для import upload) - History: план переиспользования + ревью Соннета
51 lines
2.1 KiB
JavaScript
51 lines
2.1 KiB
JavaScript
// parseZip — разбор ZIP: центральный каталог → записи {name, data, dosMs, isDir}.
|
|
// Поддерживаются методы 0 (store) и 8 (deflate). Без внешних библиотек.
|
|
|
|
import { decodeZipName } from './decode_zip_name.js';
|
|
import { inflateRaw } from './inflate_raw.js';
|
|
import { dosToMs } from './dos_to_ms.js';
|
|
|
|
export async function parseZip(buf) {
|
|
const dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
let eocd = -1;
|
|
for (let i = buf.length - 22; i >= 0; i--) {
|
|
if (dv.getUint32(i, true) === 0x06054b50) { eocd = i; break; }
|
|
}
|
|
if (eocd < 0) throw new Error('Не ZIP');
|
|
const cdSize = dv.getUint32(eocd + 12, true);
|
|
const cdOffset = dv.getUint32(eocd + 16, true);
|
|
|
|
const entries = [];
|
|
let pos = cdOffset;
|
|
const cdEnd = cdOffset + cdSize;
|
|
while (pos < cdEnd) {
|
|
if (dv.getUint32(pos, true) !== 0x02014b50) break;
|
|
const flags = dv.getUint16(pos + 8, true);
|
|
const method = dv.getUint16(pos + 10, true);
|
|
const modTime = dv.getUint16(pos + 12, true);
|
|
const modDate = dv.getUint16(pos + 14, true);
|
|
const compSize = dv.getUint32(pos + 20, true);
|
|
const nameLen = dv.getUint16(pos + 28, true);
|
|
const extraLen = dv.getUint16(pos + 30, true);
|
|
const commentLen = dv.getUint16(pos + 32, true);
|
|
const localOffset = dv.getUint32(pos + 42, true);
|
|
|
|
const nameBytes = buf.slice(pos + 46, pos + 46 + nameLen);
|
|
const name = decodeZipName(nameBytes, (flags & 0x800) !== 0);
|
|
|
|
const lhNameLen = dv.getUint16(localOffset + 26, true);
|
|
const lhExtraLen = dv.getUint16(localOffset + 28, true);
|
|
const dataStart = localOffset + 30 + lhNameLen + lhExtraLen;
|
|
const comp = buf.slice(dataStart, dataStart + compSize);
|
|
|
|
let data;
|
|
if (method === 0) data = comp;
|
|
else if (method === 8) data = await inflateRaw(comp);
|
|
else throw new Error('Метод сжатия ' + method + ' не поддерживается');
|
|
|
|
entries.push({ name, data, dosMs: dosToMs(modDate, modTime), isDir: name.endsWith('/') });
|
|
pos += 46 + nameLen + extraLen + commentLen;
|
|
}
|
|
return entries;
|
|
}
|