UNPKG

@apr144/generic-filehandle

Version:

uniform interface for accessing binary data from local files, remote HTTP resources, and browser Blob data

128 lines (110 loc) 3.68 kB
import { Buffer } from 'buffer' import { GenericFilehandle, FilehandleOptions, Stats } from './filehandle' // Using this you can "await" the file like a normal promise // https://blog.shovonhasan.com/using-promises-with-filereader/ function readBlobAsArrayBuffer(blob: Blob): Promise<ArrayBuffer> { const fileReader = new FileReader() return new Promise((resolve, reject): void => { fileReader.onerror = (): void => { fileReader.abort() reject(new Error('problem reading blob')) } fileReader.onabort = (): void => { reject(new Error('blob reading was aborted')) } fileReader.onload = (): void => { if (fileReader.result && typeof fileReader.result !== 'string') { resolve(fileReader.result) } else { reject(new Error('unknown error reading blob')) } } fileReader.readAsArrayBuffer(blob) }) } function readBlobAsText(blob: Blob): Promise<string> { const fileReader = new FileReader() return new Promise((resolve, reject): void => { fileReader.onerror = (): void => { fileReader.abort() reject(new Error('problem reading blob')) } fileReader.onabort = (): void => { reject(new Error('blob reading was aborted')) } fileReader.onload = (): void => { if (fileReader.result && typeof fileReader.result === 'string') { resolve(fileReader.result) } else { reject(new Error('unknown error reading blob')) } } fileReader.readAsText(blob) }) } /** * Blob of binary data fetched from a local file (with FileReader). * * Adapted by Robert Buels and Garrett Stevens from the BlobFetchable object in * the Dalliance Genome Explorer, which is copyright Thomas Down 2006-2011. */ export default class BlobFile implements GenericFilehandle { private blob: Blob private size: number public constructor(blob: Blob) { this.blob = blob this.size = blob.size } public async read( buffer: Buffer, offset = 0, length: number, position = 0, ): Promise<{ bytesRead: number; buffer: Buffer }> { // short-circuit a read of 0 bytes here, because browsers actually sometimes // crash if you try to read 0 bytes from a local file! if (!length) { return { bytesRead: 0, buffer } } const start = position const end = start + length const result = await readBlobAsArrayBuffer(this.blob.slice(start, end)) const resultBuffer = Buffer.from(result) const bytesCopied = resultBuffer.copy(buffer, offset) return { bytesRead: bytesCopied, buffer: resultBuffer } } public async readFile(): Promise<Buffer> public async readFile(options: BufferEncoding): Promise<string> public async readFile<T extends undefined>( options: | Omit<FilehandleOptions, 'encoding'> | (Omit<FilehandleOptions, 'encoding'> & { encoding: T }), ): Promise<Buffer> public async readFile<T extends BufferEncoding>( options: Omit<FilehandleOptions, 'encoding'> & { encoding: T }, ): Promise<string> public async readFile( options?: FilehandleOptions | BufferEncoding, ): Promise<Buffer | string> { let encoding if (typeof options === 'string') { encoding = options } else { encoding = options && options.encoding } if (encoding === 'utf8') { return readBlobAsText(this.blob) } if (encoding) { throw new Error(`unsupported encoding: ${encoding}`) } const result = await readBlobAsArrayBuffer(this.blob) return Buffer.from(result) } public async stat(): Promise<Stats> { return { size: this.size } } public async close(): Promise<void> { return } }