import { Readable } from 'stream'; import { CreateBucketCommandInput, GetObjectCommand, HeadBucketCommandInput, NotFound, PutObjectCommand, S3, S3ClientConfig, } from '@aws-sdk/client-s3'; import { StorageDriver } from './interfaces/storage-driver.interface'; export interface S3DriverOptions extends S3ClientConfig { bucketName: string; endpoint?: string; region: string; } export class S3Driver implements StorageDriver { private s3Client: S3; private bucketName: string; constructor(options: S3DriverOptions) { const { bucketName, region, endpoint, ...s3Options } = options; if (!bucketName || !region) { return; } this.s3Client = new S3({ ...s3Options, region, endpoint }); this.bucketName = bucketName; } public get client(): S3 { return this.s3Client; } async write(params: { file: Buffer | Uint8Array | string; name: string; folder: string; mimeType: string | undefined; }): Promise { const command = new PutObjectCommand({ Key: `${params.folder}/${params.name}`, Body: params.file, ContentType: params.mimeType, Bucket: this.bucketName, }); await this.s3Client.send(command); } async read(params: { folderPath: string; filename: string; }): Promise { const command = new GetObjectCommand({ Key: `${params.folderPath}/${params.filename}`, Bucket: this.bucketName, }); const file = await this.s3Client.send(command); if (!file || !file.Body || !(file.Body instanceof Readable)) { throw new Error('Unable to get file stream'); } return Readable.from(file.Body); } async checkBucketExists(args: HeadBucketCommandInput) { try { await this.s3Client.headBucket(args); return true; } catch (error) { if (error instanceof NotFound) { return false; } throw error; } } async createBucket(args: CreateBucketCommandInput) { const exist = await this.checkBucketExists({ Bucket: args.Bucket, }); if (exist) { return; } return this.s3Client.createBucket(args); } }