//Similar to extractFile.ts, but instead of receiving and returning an ArrayBuffer, works with Node.js streams. import * as stream from 'stream'; import * as zlib from 'zlib'; import { ISsnFileEntry } from '../interfaces/ISsnFileEntry'; import decryptStream from './streams/decryptStream'; import streamSetMaxLength from './streams/streamSetMaxLength'; /** Extracts the file with the given metadata from the stream. * The stream must already start at the .zip's local file header * and must transparently span across multiple disks if necessary. */ export default function extractFileStream(file: ISsnFileEntry, inputStream: stream.Readable): stream.Readable { const localFileHeader: Buffer = inputStream.read(30); //Local file header signature const magic = localFileHeader.readUInt32LE(0); if (magic !== 0x04034B50) { throw new Error(`Local file header had wrong magic; expected 0x04034B50 but got 0x${magic.toString(16).padStart(8, '0')}`); } //All fields in the local file header are copies of the central file header, so we can skip them. //FIXME: Maybe we should actually read these fields to verify that they are identical? //skip 22 bytes const localFilenameSize = localFileHeader.readUInt16LE(26); const localExtraSize = localFileHeader.readUInt16LE(28); //skip local file name and extra field inputStream.read(localFilenameSize + localExtraSize); //------------------------------------------------- let curStream = inputStream; //set max length const maxLength = streamSetMaxLength(curStream, file.compressedSize); curStream = maxLength; //pipe into decryption if file is encrypted if (file.decryptionKeys !== undefined) { const decryptTransform = decryptStream(curStream, file.decryptionKeys); curStream = decryptTransform; } //pipe into decompression const decompressTransform = zlib.createInflateRaw(); curStream.pipe(decompressTransform); curStream = decompressTransform; //set max length const maxLength2 = streamSetMaxLength(curStream, file.size); curStream = maxLength2; return curStream; }