UNPKG

@rytass/storages

Version:
103 lines (100 loc) 3.83 kB
import { PassThrough } from 'stream'; import { createHash } from 'crypto'; import { Buffer } from 'buffer'; import { fileTypeFromBuffer, fileTypeFromStream } from 'file-type'; import { ConverterManager } from '@rytass/file-converter'; const MIN_BUFFER_LENGTH = 16; class Storage { converterManager; hashAlgorithm; constructor(options){ this.converterManager = new ConverterManager(options?.converters ?? []); this.hashAlgorithm = options?.hashAlgorithm || 'sha256'; } getExtension(file) { if (file instanceof Buffer) { return fileTypeFromBuffer(file); } const extensionStream = new PassThrough(); file.pipe(extensionStream); return fileTypeFromStream(extensionStream); } async getBufferFilename(buffer) { const extension = await fileTypeFromBuffer(buffer); return [ `${createHash(this.hashAlgorithm).update(buffer).digest('hex')}${extension?.ext ? `.${extension.ext}` : ''}`, extension?.mime ?? undefined ]; } getStreamFilename(stream) { return new Promise((resolve, reject)=>{ const hashStream = new PassThrough(); const extensionStream = new PassThrough(); const getStreamHash = new Promise((subResolve)=>{ const hash = createHash(this.hashAlgorithm); hashStream.on('data', (buffer)=>{ hash.update(buffer); }); hashStream.on('end', ()=>{ subResolve(hash.digest('hex')); }); }); const getStreamFileType = new Promise((subResolve)=>{ let resolved = false; let isEnd = false; const bufferStorage = []; const waitingTasks = []; extensionStream.on('data', (buffer)=>{ if (resolved) return; bufferStorage.push(buffer); const targetBuffer = Buffer.concat(bufferStorage); if (targetBuffer.length >= MIN_BUFFER_LENGTH) { const taskIndex = waitingTasks.length; waitingTasks.push(true); fileTypeFromBuffer(targetBuffer).then((result)=>{ waitingTasks[taskIndex] = false; if (!result && !isEnd) return; resolved = true; subResolve(result); }); } else { waitingTasks.push(false); } }); extensionStream.on('end', ()=>{ isEnd = true; if (waitingTasks.every((task)=>!task) && !resolved) { resolved = true; subResolve(undefined); } }); }); Promise.all([ getStreamHash, getStreamFileType ]).then(([filename, extension])=>{ resolve([ `${filename}${extension?.ext ? `.${extension.ext}` : ''}`, extension?.mime ?? undefined ]); }).catch(reject); stream.pipe(hashStream).pipe(extensionStream); }); } write(_file, _options) { throw new Error('Method not implemented.'); } batchWrite(_files, _options) { throw new Error('Method not implemented.'); } read(_key, _options) { throw new Error('Method not implemented.'); } remove(_key) { throw new Error('Method not implemented.'); } isExists(_key) { throw new Error('Method not implemented.'); } } export { Storage };