UNPKG

pubnub

Version:

Publish & Subscribe Real-time Messaging with PubNub

257 lines (217 loc) 6.75 kB
/** * Node.js {@link PubNub} File object module. */ import { Readable, PassThrough } from 'stream'; import { Buffer } from 'buffer'; import { basename } from 'path'; import fs from 'fs'; import { PubNubFileInterface } from '../../core/types/file'; // -------------------------------------------------------- // ------------------------ Types ------------------------- // -------------------------------------------------------- // region Types /** * PubNub File instance creation parameters. */ export type PubNubFileParameters<StringEncoding extends BufferEncoding = BufferEncoding> = { /** * Readable stream represents file object content. */ stream?: Readable; /** * Buffer or string represents file object content. */ data?: Buffer | ArrayBuffer | string; /** * String {@link PubNubFileParameters#data|data} encoding. * * @default `utf8` */ encoding?: StringEncoding; /** * File object name. */ name: string; /** * File object content type. */ mimeType?: string; }; // endregion /** * Node.js implementation for {@link PubNub} File object. * * **Important:** Class should implement constructor and class fields from {@link PubNubFileConstructor}. */ export default class PubNubFile implements PubNubFileInterface { // region Class properties /** * Whether {@link Blob} data supported by platform or not. */ static supportsBlob = false; /** * Whether {@link File} data supported by platform or not. */ static supportsFile = false; /** * Whether {@link Buffer} data supported by platform or not. */ static supportsBuffer = true; /** * Whether {@link Stream} data supported by platform or not. */ static supportsStream = true; /** * Whether {@link String} data supported by platform or not. */ static supportsString = true; /** * Whether {@link ArrayBuffer} supported by platform or not. */ static supportsArrayBuffer = true; /** * Whether {@link PubNub} File object encryption supported or not. */ static supportsEncryptFile = true; /** * Whether `File Uri` data supported by platform or not. */ static supportsFileUri = false; // endregion // region Instance properties /** * File object content source. */ readonly data: Readable | Buffer; /** * File object content length. */ contentLength?: number; /** * File object content type. */ mimeType: string; /** * File object name. */ name: string; // endregion static create(file: PubNubFileParameters) { return new PubNubFile(file); } constructor(file: PubNubFileParameters) { const { stream, data, encoding, name, mimeType } = file; let fileData: Readable | Buffer | undefined; let contentLength: number | undefined; let fileMimeType: string | undefined; let fileName: string | undefined; if (stream && stream instanceof Readable) { fileData = stream; if (stream instanceof fs.ReadStream && typeof stream.path === 'string') { fileName = basename(stream.path); contentLength = fs.statSync(stream.path).size; } } else if (data instanceof Buffer) { contentLength = data.length; // Copy content of the source Buffer. fileData = Buffer.alloc(contentLength!); data.copy(fileData); } else if (data instanceof ArrayBuffer) { contentLength = data.byteLength; fileData = Buffer.from(data); } else if (typeof data === 'string') { fileData = Buffer.from(data, encoding ?? 'utf8'); contentLength = fileData.length; } if (contentLength) this.contentLength = contentLength; if (mimeType) fileMimeType = mimeType; else fileMimeType = 'application/octet-stream'; if (name) fileName = basename(name); if (fileData === undefined) throw new Error("Couldn't construct a file out of supplied options."); if (fileName === undefined) throw new Error("Couldn't guess filename out of the options. Please provide one."); this.mimeType = fileMimeType; this.data = fileData; this.name = fileName; } /** * Convert {@link PubNub} File object content to {@link Buffer}. * * @returns Asynchronous results of conversion to the {@link Buffer}. */ async toBuffer(): Promise<Buffer> { if (!(this.data instanceof Readable)) return this.data; const stream = this.data; return new Promise((resolve, reject) => { const chunks: Uint8Array[] = []; stream.on('data', (chunk) => { chunks.push(chunk); }); stream.on('end', () => { resolve(Buffer.concat(chunks)); }); // Handle any errors during streaming stream.on('error', (error) => reject(error)); }); } /** * Convert {@link PubNub} File object content to {@link ArrayBuffer}. * * @returns Asynchronous results of conversion to the {@link ArrayBuffer}. */ async toArrayBuffer(): Promise<ArrayBuffer> { return this.toBuffer().then((buffer) => buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.length)); } /** * Convert {@link PubNub} File object content to {@link string}. * * @returns Asynchronous results of conversion to the {@link string}. */ async toString(encoding: BufferEncoding = 'utf8'): Promise<string> { return this.toBuffer().then((buffer) => buffer.toString(encoding)); } /** * Convert {@link PubNub} File object content to {@link Readable} stream. * * @returns Asynchronous results of conversion to the {@link Readable} stream. */ async toStream() { if (this.data instanceof Readable) { const stream = new PassThrough(); this.data.pipe(stream); return stream; } return this.toBuffer().then( (buffer) => new Readable({ read() { this.push(buffer); this.push(null); }, }), ); } /** * Convert {@link PubNub} File object content to {@link File}. * * @throws Error because {@link File} not available in Node.js environment. */ async toFile() { throw new Error('This feature is only supported in browser environments.'); } /** * Convert {@link PubNub} File object content to file `Uri`. * * @throws Error because file `Uri` not available in Node.js environment. */ async toFileUri(): Promise<Record<string, unknown>> { throw new Error('This feature is only supported in React Native environments.'); } /** * Convert {@link PubNub} File object content to {@link Blob}. * * @throws Error because {@link Blob} not available in Node.js environment. */ async toBlob() { throw new Error('This feature is only supported in browser environments.'); } }