cloud-object-storage-lib
Version:
Object Storage Manager for node
218 lines (191 loc) • 7.29 kB
text/typescript
import S3 from 'aws-sdk/clients/s3'
import fs from 'fs'
import fsPromise from 'fs/promises'
import stream from 'stream'
import { TStorageConfig } from '../interfaces'
export default class S3Storage {
s3: S3
constructor(config: TStorageConfig) {
this.s3 = new S3({
accessKeyId: config.accessKey,
secretAccessKey: config.secretKey,
endpoint: config.endPoint,
sslEnabled: config.useSSL,
region: config.region,
})
}
async uploadBuffer(options: { data: Buffer; destFile: string }) {
if (!options || !options.data || !options.destFile) throw new Error('wrong options provided to upload')
try {
const bk = options.destFile.split('/')[1]
const fileObject = options.destFile.split('/' + bk + '/')[1]
console.log('uploading buffer to' + options.destFile)
await this.s3.upload({ Bucket: bk, Key: fileObject, Body: options.data }).promise()
} catch (err) {
console.error('(uploadBuffer)', err)
throw err
}
}
async uploadFile(options: { sourceFile: string; destFile: string; removeSource?: boolean }) {
if (!options || !options.sourceFile || !options.destFile) throw new Error('wrong options provided to upload')
try {
const stat = await fsPromise.stat(options.sourceFile)
const mb = Math.round(stat.size / 1000000)
if (mb < 2000) {
const data = await fsPromise.readFile(options.sourceFile)
await this.uploadBuffer({ data, destFile: options.destFile })
} else {
console.log(`uploading large file (${mb}MB) to ${options.destFile}`)
// function uploadLargeFile(S3) {
// return new Promise((resolve, reject) => {
// const readStream = fs.createReadStream(options.sourceFile)
// function s3StreamUploader() {
// const pass = new stream.PassThrough()
// const bk = options.destFile.split('/')[1]
// const fileObject = options.destFile.split('/' + bk + '/')[1]
// const params = {
// Bucket: bk,
// Key: fileObject,
// Body: pass
// }
// S3.upload(params, function(error, data) {
// if (error) return reject(error)
// console.info('uploading part for ' + fileObject)
// })
// S3.on('uploaded', function(details) {
// resolve(true)
// })
// return pass
// }
// readStream.pipe(s3StreamUploader())
// })
// }
// try {
// await uploadLargeFile(this.s3)
// } catch (err) {
// throw err
// }
const uploadStream = () => {
const bk = options.destFile.split('/')[1]
const fileObject = options.destFile.split('/' + bk + '/')[1]
const pass = new stream.PassThrough()
console.info('uploading part of ' + fileObject)
return {
writeStream: pass,
promise: this.s3.upload({ Bucket: bk, Key: fileObject, Body: pass }).promise(),
}
}
const { writeStream, promise } = uploadStream()
const readStream = fs.createReadStream(options.sourceFile)
readStream.pipe(writeStream)
await promise
}
if (options.removeSource) {
try {
await fsPromise.rm(options.sourceFile)
} catch (err) {
console.error(`uploadToS3: Error while removing source ${options.sourceFile} (this could be just a warning)`)
}
}
console.log(`uploading large file completed to ${options.destFile}`)
return { ok: true }
} catch (err) {
console.error(`uploadToS3: Error while trying to upload file ${options.sourceFile} to ${options.destFile}`, err)
throw err
}
}
async getBuffer(sourceFile: string) {
if (!sourceFile) throw new Error('No sourceFile Provided')
const destFolder = await fsPromise.mkdtemp('/tmp/miniodwnlbuf')
try {
const bk = sourceFile.split('/')[1]
const fileObject = sourceFile.split('/' + bk + '/')[1]
const buf = await this.s3.getObject({ Bucket: bk, Key: fileObject }).promise()
return buf.Body as Buffer
} catch (err) {
console.error(`getBuffer: Error while trying to download file from ${sourceFile}`)
throw err
} finally {
fsPromise.rm(destFolder, { recursive: true }).catch(console.error)
}
}
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.s3.deleteObject({ Bucket: bk, Key: fileObject }).promise()
return { ok: true }
} catch (err) {
throw err
}
}
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')
try {
const data = await this.getBuffer(options.sourceFile)
await fsPromise.writeFile(options.destFile, data as any)
} catch (err) {
console.error(
`downloadFile: Error while trying to download file from ${options.sourceFile} to ${options.destFile}`,
)
throw err
}
return { ok: true }
}
getPresigned(options: { path: string; ssl?: boolean }) {
if (!options.path) throw new Error('No s3 path Provided')
const bk = options.path.split('/')[1]
const fileObject = options.path.split('/' + bk + '/')[1]
try {
const presignedUrl = this.s3.getSignedUrl('getObject', { Bucket: bk, Key: fileObject, Expires: 6 * 60 * 60 })
return { presignedUrl: presignedUrl }
} catch (err) {
throw err
}
}
getPresigned4Upload(options: { path: string; ssl?: boolean; ContentType?: string }) {
if (!options.path) throw new Error('No s3 path Provided')
const bk = options.path.split('/')[1]
const fileObject = options.path.split('/' + bk + '/')[1]
try {
const obj: { Bucket: string; Key: string; Expires: number; ContentType?: string } = {
Bucket: bk,
Key: fileObject,
Expires: 6 * 60 * 60,
}
if (options.ContentType) obj['ContentType'] = options.ContentType
const presignedUrl = this.s3.getSignedUrl('putObject', obj)
return { presignedUrl: presignedUrl }
} catch (err) {
throw err
}
}
}