Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- class ZipFetcher {
- /**
- * Класс для загрузки ZIP-файла и анализа его содержимого в реальном времени
- */
- constructor (reader, abortController) {
- this.reader = reader;
- this.abortController = abortController;
- this.buffer = [];
- this.utf8Decoder = new TextDecoder('utf-8');
- }
- static async fromUrl(url) {
- /**
- * Точка входа, инициирует загрузку файла и получает ридер для передачи в
- * конструктор
- */
- const abortController = new AbortController();
- const abortSignal = abortController.signal;
- const response = await fetch(url, { abortSignal });
- const reader = response.body.getReader();
- return new ZipFetcher(reader, abortController);
- }
- async fetchUntilFileName(fileName) {
- /**
- * Последовательно получает файлы, хранящиеся в архиве
- */
- let localFile;
- do {
- localFile = await this.fetchLocalFile()
- }
- while (localFile.header.fileName !== fileName)
- // TODO: {throw new Error(`${fileName} not found`)};
- this.stop();
- return localFile;
- }
- async fetchLocalFile () {
- /**
- * Складывает заголовок и данные файла в один объект
- */
- const localFile = new Object();
- localFile.header = await this.fetchLocalFileHeader();
- const dataSize = localFile.header.compressedDataSize;
- localFile.data = await this.fetchLocalFileData(dataSize);
- return localFile;
- }
- async fetchLocalFileHeader () {
- /**
- * Получает первые байты из буфера, в которых хранится заголовок файла,
- * и парсит их
- */
- const localFileHeader = new Object();
- const constPartSize = 30;
- await this.fetchBufferToLength(constPartSize);
- const constPart =
- this.parseHeaderConstPart(this.buffer.splice(0, constPartSize));
- Object.assign(localFileHeader, constPart);
- localFileHeader.varPartSize =
- localFileHeader.fileNameSize + localFileHeader.extraFieldSize;
- await this.fetchBufferToLength(localFileHeader.varPartSize);
- const varPartArray = this.buffer.splice(0, localFileHeader.varPartSize);
- const varPart =
- this.parseHeaderVarPart(varPartArray, localFileHeader.fileNameSize);
- Object.assign(localFileHeader, varPart)
- return localFileHeader;
- }
- parseHeaderConstPart (headerConstPart) {
- /**
- * Парсит первую часть заголовка, длина которой всегда 30 байт
- */
- const constPart = new Object();
- const headerConstPartView =
- new DataView(new Uint8Array(headerConstPart).buffer);
- constPart.signature = headerConstPartView.getUint32(0, true);
- if (constPart.signature != 0x04034b50) {
- throw new Error(
- `Unexpected file signature - ${constPart.signature.toString(16)}`
- );
- }
- constPart.compressedDataSize = headerConstPartView.getUint32(18, true);
- constPart.fileNameSize = headerConstPartView.getUint16(26, true);
- constPart.extraFieldSize = headerConstPartView.getUint16(28, true);
- return constPart;
- }
- parseHeaderVarPart (headerVarPart, fileNameSize) {
- /**
- * Парсит вторую часть заголовка, где хранится имя файла и опциональное
- * поле переменной длины
- */
- const varPart = new Object();
- const fileNameBytes = new Uint8Array(headerVarPart.splice(0, fileNameSize));
- varPart.fileName = this.utf8Decoder.decode(fileNameBytes);
- varPart.extraField = new Uint8Array(headerVarPart);
- return varPart;
- }
- async fetchLocalFileData(dataSize) {
- /**
- * Получает данные из тела файла и разжимает их алгоритмом Inflate
- */
- await this.fetchBufferToLength(dataSize);
- const compressedData = new Uint8Array(this.buffer.splice(0, dataSize));
- return new Zlib.RawInflate(compressedData).decompress();
- }
- async fetchBufferToLength(length) {
- /**
- * Подгружает новые данные в буфер из ридера пока длина
- * буфера не станет равна dataSize байт (элементов).
- */
- let chunk, readerDone;
- while (this.buffer.length < length && !readerDone) {
- ({ value: chunk, done: readerDone } = await this.reader.read());
- chunk && this.buffer.push(...chunk);
- }
- if (readerDone && buffer.length < length) {
- throw new Error('Unexpected EOF')
- }
- }
- stop() {
- /**
- * Закывает ридер и отменяет загрузку архива
- */
- this.reader.cancel();
- this.abortController.abort();
- }
- }
- async function run() {
- const fetcher = await ZipFetcher.fromUrl("http://192.168.0.2/share/archive.zip");
- foundFile = fetcher.fetchUntilFileName('file.txt');
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement