UNPKG

cloud-object-storage-lib

Version:

Object Storage Manager for node

292 lines (263 loc) 9.62 kB
import fsPromise from 'fs/promises' import mime from 'mime-types' import { Client, CopyConditions, ItemBucketMetadata } from 'minio' import moment from 'moment' import path from 'path' import { IMinioLs } from './interfaces' import { TStorageConfig } from '../interfaces' export default class MinioStorage extends Client { constructor(config: TStorageConfig) { super({ endPoint: config.endPoint, port: config.port, useSSL: config.useSSL, accessKey: config.accessKey, secretKey: config.secretKey, region: config.region, }) } ls(minioDirPath: string, options?: { recursive?: boolean; sorted?: boolean }) { if (!minioDirPath) throw new Error('wrong options provided for ls action (Minio)') if (!options) options = {} if (!options.recursive) options.recursive = false return new Promise<IMinioLs[]>((resolve, reject) => { const bk = minioDirPath.split('/')[1] let prefix = minioDirPath.split('/' + bk + '/')[1] if (prefix && prefix[prefix.length - 1] !== '/') prefix = prefix + '/' const stream = this.listObjectsV2(bk, prefix, options?.recursive) const fileList: IMinioLs[] = [] stream.on('data', (obj) => { if (obj && obj.lastModified && obj.name) { const dateOfFile = moment(obj.lastModified) fileList.push({ filePath: '/' + bk + '/' + obj.name, fileName: path.basename(obj.name), lastModified: dateOfFile.format(), unixTime: dateOfFile.valueOf(), }) } }) stream.on('end', () => { if (options?.sorted) fileList.sort((f) => f.unixTime) resolve(fileList) }) stream.on('error', (err) => { reject(err) }) }) } async copyFolderOnMinio(opts: { sourceFolder: string; destFolder: string }) { try { if (!opts || !opts.sourceFolder || !opts.destFolder) throw new Error('wrong moveOnMinio opts') const allMinioFiles = await this.ls(opts.sourceFolder) const allMinioFilesPath = allMinioFiles.map((m) => m.filePath) for (const minioFile of allMinioFilesPath) { await this.copyFileOnMinio({ sourceFile: minioFile, destFile: opts.destFolder + '/' + path.basename(minioFile), }) } } catch (err) { console.error(err) throw err } } async moveFolderOnMinio(opts: { sourceFolder: string; destFolder: string }) { await this.copyFolderOnMinio(opts) await this.removeFolderFromMinio(opts.sourceFolder) } async moveFileOnMinio(opts: { sourceFile: string; destFile: string }) { await this.copyFileOnMinio(opts) await this.removeFile(opts.sourceFile) } async copyFileOnMinio(opts: { sourceFile: string; destFile: string }) { try { if (!opts || !opts.sourceFile || !opts.destFile) throw new Error('wrong moveOnMinio opts') const bkSource = opts.sourceFile.split('/')[1] const bkDest = opts.destFile.split('/')[1] if (bkSource === bkDest) { const fileObjectDest = opts.destFile.split('/' + bkSource + '/')[1] const fileObjectSource = opts.sourceFile.split('/' + bkSource + '/')[1] var conds = new CopyConditions() // const ob=await this.statObject(bkSource, fileObjectSource) // conds.setMatchETag(ob.etag) const copyo = await this.copyObject(bkSource, fileObjectDest, opts.sourceFile, conds) // console.log('Successfully copied the object:') } else { const buff = await this.getBuffer(opts.sourceFile) await this.uploadBuffer({ destFile: opts.destFile, data: buff }) } } catch (err) { console.error(err) throw err } } async statFileOnMinio(minioFilePath: string) { if (!minioFilePath) throw new Error('wrong options provided for remove action (Minio)') try { const bk = minioFilePath.split('/')[1] const fileObject = minioFilePath.split('/' + bk + '/')[1] return await this.statObject(bk, fileObject) } catch (err) { throw err } } async checkIfExistsOnMinio(minioFilePath: string): Promise<boolean> { if (!minioFilePath) throw new Error('wrong options provided for remove action (Minio)') try { const bk = minioFilePath.split('/')[1] const fileObject = minioFilePath.split('/' + bk + '/')[1] try { const statFile = await this.statObject(bk, fileObject) // await this.removeObject(bk, fileObject) return true } catch (err) { return false } } catch (err) { throw err } } async removeFolderFromMinio(minioFolder: string) { // TODO: to be tested try { if (!minioFolder) throw new Error('wrong options provided for remove action (Minio)') const files = await this.ls(minioFolder) for (const file of files) { await this.removeFile(file.filePath) } } catch (err) { console.error(err) throw err } } async removeFile(file: string) { if (!file) throw new Error('wrong options provided for remove action (Minio)') try { const bk = file.split('/')[1] const fileObject = file.split('/' + bk + '/')[1] await this.removeObject(bk, fileObject) return { ok: true } } catch (err) { throw err } } async uploadBuffer(options: { data: Buffer; destFile: string }) { if (!options || !options.data || !options.destFile) throw new Error('wrong options provided to upload') try { const tmpFile = '/tmp/' + Date.now() + '_' + path.basename(options.destFile) await fsPromise.writeFile(tmpFile, options.data) await this.uploadFile({ sourceFile: tmpFile, destFile: options.destFile, removeSource: true, }) } catch (err) { console.error('(uploadBuffer)') throw err } } async uploadFile(options: { sourceFile: string destFile: string removeSource?: boolean meta?: ItemBucketMetadata }) { if (!options || !options.sourceFile || !options.destFile) throw new Error('wrong options provided to upload') if (!options.meta || !options.meta['Content-Type']) { options.meta = { 'Content-Type': mime.lookup(path.basename(options.destFile)) || 'application/octet-stream', } } const bk = options.destFile.split('/')[1] const fileObject = options.destFile.split('/' + bk + '/')[1] try { await this.fPutObject(bk, fileObject, options.sourceFile, options.meta) if (options.removeSource) { try { await fsPromise.rm(options.sourceFile, { recursive: true }) } catch (err) { console.error( `uploadToMinio: Error while removing source ${options.sourceFile} (this could be just a warning)`, ) } } return { ok: true } } catch (err) { console.error(`uploadToMinio: Error while trying to upload file ${options.sourceFile} to ${options.destFile}`) throw err } } async getBuffer(sourceFile: string) { if (!sourceFile) throw new Error('No sourceFile Provided') const destFolder = await fsPromise.mkdtemp('/tmp/miniodwnlbuf') try { const destFile = path.join(destFolder, path.basename(sourceFile)) await this.downloadFile({ sourceFile, destFile }) const buf = await fsPromise.readFile(destFile) return buf } catch (err) { console.error(`getBufferFromMinio: Error while trying to download file from ${sourceFile}`) throw err } finally { fsPromise.rm(destFolder, { recursive: true }).catch(console.error) } } async readFile( sourceFile: string, encoding: BufferEncoding = 'utf8', callback?: (error: Error | null, content?: any) => any, ) { try { const buf = await this.getBuffer(sourceFile) const content = buf.toString(encoding) if (callback) callback(null, content) else return content } catch (err: any) { if (callback) callback(err as Error | null) else throw err } } async readJson( sourceFile: string, encoding: BufferEncoding = 'utf8', callback?: (error: Error | null, content?: any) => any, ) { if (callback) { await this.readFile(sourceFile, encoding, (error, content) => { if (error) callback(error) else callback(null, JSON.parse(content)) }) } else return JSON.parse((await this.readFile(sourceFile)) as string) } async downloadFile(options: { sourceFile: string; destFile: string }) { if (!options.sourceFile) throw new Error('No sourceFile Provided') const bk = options.sourceFile.split('/')[1] const fileObject = options.sourceFile.split('/' + bk + '/')[1] try { await this.fGetObject(bk, fileObject, options.destFile) } catch (err) { console.error( `downloadFromMinio: Error while trying to download file from ${options.sourceFile} to ${options.destFile}`, ) throw err } return { ok: true } } async getPresigned(options: { path: string; ssl?: boolean }) { if (!options.path) throw new Error('No minioFilePath Provided') let protocol = 'http' if (options.ssl) protocol = 'https' const bk = options.path.split('/')[1] const fileObject = options.path.split('/' + bk + '/')[1] try { const presignedUrl = await this.presignedGetObject(bk, fileObject, 6 * 60 * 60) return { presignedUrl: presignedUrl } } catch (err) { throw err } } getPresigned4Upload(options: { path: string; ssl?: boolean; ContentType?: string }) { throw new Error('todo') } }