Compare commits

...

6 Commits

Author SHA1 Message Date
b925e5e662 11.0.0 2023-11-06 11:15:12 +01:00
98a5d2c94d BREAKING CHANGE(core): update 2023-11-06 11:15:11 +01:00
0e735cba20 10.0.40 2023-11-04 20:54:14 +01:00
f815457801 fix(core): update 2023-11-04 20:54:13 +01:00
f7e47ae354 10.0.39 2023-11-04 20:43:55 +01:00
684e893801 fix(core): update 2023-11-04 20:43:54 +01:00
3 changed files with 73 additions and 13 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "@push.rocks/smartfile", "name": "@push.rocks/smartfile",
"private": false, "private": false,
"version": "10.0.38", "version": "11.0.0",
"description": "offers smart ways to work with files in nodejs", "description": "offers smart ways to work with files in nodejs",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartfile', name: '@push.rocks/smartfile',
version: '10.0.38', version: '11.0.0',
description: 'offers smart ways to work with files in nodejs' description: 'offers smart ways to work with files in nodejs'
} }

View File

@ -3,7 +3,7 @@ import * as smartfileFs from './fs.js';
import * as smartfileFsStream from './fsstream.js'; import * as smartfileFsStream from './fsstream.js';
import { Readable } from 'stream'; import { Readable } from 'stream';
type StreamSource = () => Promise<Readable>; type TStreamSource = (streamFile: StreamFile) => Promise<Readable>;
/** /**
* The StreamFile class represents a file as a stream. * The StreamFile class represents a file as a stream.
@ -12,9 +12,14 @@ type StreamSource = () => Promise<Readable>;
export class StreamFile { export class StreamFile {
// INSTANCE // INSTANCE
relativeFilePath?: string; relativeFilePath?: string;
private streamSource: StreamSource; private streamSource: TStreamSource;
private constructor(streamSource: StreamSource, relativeFilePath?: string) { // enable stream based multi use
private cachedStreamBuffer?: Buffer;
public multiUse: boolean;
public used: boolean = false;
private constructor(streamSource: TStreamSource, relativeFilePath?: string) {
this.streamSource = streamSource; this.streamSource = streamSource;
this.relativeFilePath = relativeFilePath; this.relativeFilePath = relativeFilePath;
} }
@ -22,32 +27,84 @@ export class StreamFile {
// STATIC // STATIC
public static async fromPath(filePath: string): Promise<StreamFile> { public static async fromPath(filePath: string): Promise<StreamFile> {
const streamSource = () => Promise.resolve(smartfileFsStream.createReadStream(filePath)); const streamSource: TStreamSource = async (stremFileArg) => smartfileFsStream.createReadStream(filePath);
return new StreamFile(streamSource, filePath); const streamFile = new StreamFile(streamSource, filePath);
streamFile.multiUse = true;
return streamFile;
} }
public static async fromUrl(url: string): Promise<StreamFile> { public static async fromUrl(url: string): Promise<StreamFile> {
const streamSource = async () => plugins.smartrequest.getStream(url); // Replace with actual plugin method const streamSource: TStreamSource = async (streamFileArg) => plugins.smartrequest.getStream(url); // Replace with actual plugin method
return new StreamFile(streamSource); const streamFile = new StreamFile(streamSource);
streamFile.multiUse = true;
return streamFile;
} }
public static fromBuffer(buffer: Buffer, relativeFilePath?: string): StreamFile { public static fromBuffer(buffer: Buffer, relativeFilePath?: string): StreamFile {
const streamSource = () => { const streamSource: TStreamSource = async (streamFileArg) => {
const stream = new Readable(); const stream = new Readable();
stream.push(buffer); stream.push(buffer);
stream.push(null); // End of stream stream.push(null); // End of stream
return Promise.resolve(stream); return stream;
}; };
return new StreamFile(streamSource, relativeFilePath); const streamFile = new StreamFile(streamSource, relativeFilePath);
streamFile.multiUse = true;
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.
* @returns A StreamFile instance.
*/
public static fromStream(stream: Readable, relativeFilePath?: string, multiUse: boolean = false): StreamFile {
const streamSource: TStreamSource = (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);
streamFile.multiUse = multiUse;
// If multi-use is enabled, cache the stream when it's first read
if (multiUse) {
const chunks: Buffer[] = [];
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;
} }
// METHODS // METHODS
private 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. * Creates a new readable stream from the source.
*/ */
public async createReadStream(): Promise<Readable> { public async createReadStream(): Promise<Readable> {
return this.streamSource(); return this.streamSource(this);
} }
/** /**
@ -55,6 +112,7 @@ export class StreamFile {
* @param filePathArg The file path where the stream should be written. * @param filePathArg The file path where the stream should be written.
*/ */
public async writeToDisk(filePathArg: string): Promise<void> { public async writeToDisk(filePathArg: string): Promise<void> {
this.checkMultiUse();
const readStream = await this.createReadStream(); const readStream = await this.createReadStream();
const writeStream = smartfileFsStream.createWriteStream(filePathArg); const writeStream = smartfileFsStream.createWriteStream(filePathArg);
@ -67,12 +125,14 @@ export class StreamFile {
} }
public async writeToDir(dirPathArg: string) { public async writeToDir(dirPathArg: string) {
this.checkMultiUse();
const filePath = plugins.path.join(dirPathArg, this.relativeFilePath); const filePath = plugins.path.join(dirPathArg, this.relativeFilePath);
await smartfileFs.ensureDir(plugins.path.parse(filePath).dir); await smartfileFs.ensureDir(plugins.path.parse(filePath).dir);
return this.writeToDisk(filePath); return this.writeToDisk(filePath);
} }
public async getContentAsBuffer() { public async getContentAsBuffer() {
this.checkMultiUse();
const done = plugins.smartpromise.defer<Buffer>(); const done = plugins.smartpromise.defer<Buffer>();
const readStream = await this.createReadStream(); const readStream = await this.createReadStream();
const chunks: Buffer[] = []; const chunks: Buffer[] = [];