UNPKG

@push.rocks/smartfile

Version:

High-level file representation classes (SmartFile, StreamFile, VirtualDirectory) for efficient in-memory file management in Node.js using TypeScript. Works seamlessly with @push.rocks/smartfs for filesystem operations.

208 lines 17 kB
import * as plugins from './plugins.js'; import { Readable, Writable } from 'stream'; /** * The StreamFile class represents a file as a stream. * It allows creating streams from a file path, a URL, or a buffer. * Use SmartFileFactory to create instances of this class. */ export class StreamFile { // STATIC static async fromPath(filePath, smartFs) { if (!smartFs) { throw new Error('No SmartFs instance available. Create StreamFile through SmartFileFactory.'); } const streamSource = async (streamFileArg) => { return await streamFileArg.smartFs.file(filePath).readStream(); }; const streamFile = new StreamFile(streamSource, filePath, smartFs); streamFile.multiUse = true; streamFile.byteLengthComputeFunction = async () => { const stats = await smartFs.file(filePath).stat(); return stats.size; }; return streamFile; } static async fromUrl(url, smartFs) { const streamSource = async (streamFileArg) => { const response = await plugins.smartrequest.SmartRequest.create() .url(url) .get(); const responseStream = response.stream(); if (!responseStream) { throw new Error(`No response stream available for ${url}`); } return responseStream; }; const streamFile = new StreamFile(streamSource, undefined, smartFs); streamFile.multiUse = true; streamFile.byteLengthComputeFunction = async () => { const response = await plugins.smartrequest.SmartRequest.create() .url(url) .accept('binary') .get(); const buffer = Buffer.from(await response.arrayBuffer()); return buffer.length; }; return streamFile; } static fromBuffer(buffer, relativeFilePath, smartFs) { const streamSource = async (streamFileArg) => { const stream = new Readable(); stream.push(buffer); stream.push(null); // End of stream return stream; }; const streamFile = new StreamFile(streamSource, relativeFilePath, smartFs); streamFile.multiUse = true; streamFile.byteLengthComputeFunction = async () => buffer.length; return streamFile; } /** * Creates a StreamFile from an existing Readable stream with an option for multiple uses. * @param stream A Node.js Readable stream. * @param relativeFilePath Optional file path for the stream. * @param multiUse If true, the stream can be read multiple times, caching its content. * @param smartFs Optional SmartFs instance for filesystem operations * @returns A StreamFile instance. */ static fromStream(stream, relativeFilePath, multiUse = false, smartFs) { const streamSource = (streamFileArg) => { if (streamFileArg.multiUse) { // If multi-use is enabled and we have cached content, create a new readable stream from the buffer const bufferedStream = new Readable(); bufferedStream.push(streamFileArg.cachedStreamBuffer); bufferedStream.push(null); // No more data to push return Promise.resolve(bufferedStream); } else { return Promise.resolve(stream); } }; const streamFile = new StreamFile(streamSource, relativeFilePath, smartFs); streamFile.multiUse = multiUse; // If multi-use is enabled, cache the stream when it's first read if (multiUse) { const chunks = []; stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); stream.on('end', () => { streamFile.cachedStreamBuffer = Buffer.concat(chunks); }); // It's important to handle errors that may occur during streaming stream.on('error', (err) => { console.error('Error while caching stream:', err); }); } return streamFile; } // INSTANCE relativeFilePath; streamSource; smartFs; // enable stream based multi use cachedStreamBuffer; multiUse = false; used = false; byteLengthComputeFunction; constructor(streamSource, relativeFilePath, smartFs) { this.streamSource = streamSource; this.relativeFilePath = relativeFilePath; this.smartFs = smartFs; } // METHODS checkMultiUse() { if (!this.multiUse && this.used) { throw new Error('This stream can only be used once.'); } this.used = true; } /** * Creates a new readable stream from the source. */ async createReadStream() { const stream = await this.streamSource(this); // Check if it's a Web ReadableStream and convert to Node.js Readable if (stream && typeof stream.getReader === 'function') { // This is a Web ReadableStream, convert it to Node.js Readable return Readable.fromWeb(stream); } // It's already a Node.js Readable stream return stream; } /** * Writes the stream to the disk at the specified path. * @param filePathArg The file path where the stream should be written. */ async writeToDisk(filePathArg) { if (!this.smartFs) { throw new Error('No SmartFs instance available. Create StreamFile through SmartFileFactory.'); } this.checkMultiUse(); const readStream = await this.createReadStream(); let writeStream = await this.smartFs.file(filePathArg).writeStream(); // Check if it's a Web WritableStream and convert to Node.js Writable if (writeStream && typeof writeStream.getWriter === 'function') { // This is a Web WritableStream, convert it to Node.js Writable writeStream = Writable.fromWeb(writeStream); } return new Promise((resolve, reject) => { readStream.pipe(writeStream); readStream.on('error', reject); writeStream.on('error', reject); writeStream.on('finish', resolve); }); } async writeToDir(dirPathArg) { if (!this.smartFs) { throw new Error('No SmartFs instance available. Create StreamFile through SmartFileFactory.'); } this.checkMultiUse(); if (!this.relativeFilePath) { throw new Error('Cannot write stream to directory without a relative file path.'); } const filePath = plugins.path.join(dirPathArg, this.relativeFilePath); const dirPath = plugins.path.parse(filePath).dir; await this.smartFs.directory(dirPath).recursive().create(); return this.writeToDisk(filePath); } async getContentAsBuffer() { this.checkMultiUse(); const done = plugins.smartpromise.defer(); const readStream = await this.createReadStream(); const chunks = []; readStream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); readStream.on('error', done.reject); readStream.on('end', () => { const contentBuffer = Buffer.concat(chunks); done.resolve(contentBuffer); }); return done.promise; } async getContentAsString(formatArg = 'utf8') { const contentBuffer = await this.getContentAsBuffer(); return contentBuffer.toString(formatArg); } /** * Returns the size of the file content in bytes. */ async getSize() { if (this.byteLengthComputeFunction) { return this.byteLengthComputeFunction(); } else { return null; } } /** * Converts the StreamFile to a SmartFile by loading content into memory */ async toSmartFile() { const { SmartFile } = await import('./classes.smartfile.js'); const buffer = await this.getContentAsBuffer(); return new SmartFile({ path: this.relativeFilePath || 'stream', contentBuffer: buffer, base: process.cwd() }, this.smartFs); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5zdHJlYW1maWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5zdHJlYW1maWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBSTVDOzs7O0dBSUc7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQUNyQixTQUFTO0lBRUYsTUFBTSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsUUFBZ0IsRUFBRSxPQUFhO1FBQzFELElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNiLE1BQU0sSUFBSSxLQUFLLENBQUMsNEVBQTRFLENBQUMsQ0FBQztRQUNoRyxDQUFDO1FBRUQsTUFBTSxZQUFZLEdBQWtCLEtBQUssRUFBRSxhQUFhLEVBQUUsRUFBRTtZQUMxRCxPQUFPLE1BQU0sYUFBYSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDakUsQ0FBQyxDQUFDO1FBQ0YsTUFBTSxVQUFVLEdBQUcsSUFBSSxVQUFVLENBQUMsWUFBWSxFQUFFLFFBQVEsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUNuRSxVQUFVLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQztRQUMzQixVQUFVLENBQUMseUJBQXlCLEdBQUcsS0FBSyxJQUFJLEVBQUU7WUFDaEQsTUFBTSxLQUFLLEdBQUcsTUFBTSxPQUFPLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ2xELE9BQU8sS0FBSyxDQUFDLElBQUksQ0FBQztRQUNwQixDQUFDLENBQUM7UUFDRixPQUFPLFVBQVUsQ0FBQztJQUNwQixDQUFDO0lBRU0sTUFBTSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBVyxFQUFFLE9BQWE7UUFDcEQsTUFBTSxZQUFZLEdB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