Implementing file upload service
Requirement:
I needed a generic file uploader service which have the following functionalities:
- generate signed urls
- get the files from a folder
- delete a file
- delete a folder
Also, I might change the service provider later. Currently, I wanted to use ImageKit but later I might change to dropbox or aws s3 or onedrive, etc. But, I want minimal changes in my code with a single configuration. Some thing like, I pass an argument as ‘imagekit’ or ‘gdrive’ or ‘awss3’ and then it will get uploaded to the respective clouds.
Action:
So, I used Interface.
FileUploader interface:
I have added a generic type as I would be using multiple providers in future and they might not return a specific type of result. I might fix a return type, but I do not want to do that just to see what happens.
export interface IFileUploader { generateSignedURLs<T>(urlCount: number): T; // All other functions...}
Implementing interface:
Injectable()export class ImageKitUploader implements IFileUploader { private publicKey: string; private client: ImageKit; constructor(private readonly configService: ConfigService) { /* 1. Added "!" as we are validating the keys before starting the app 2. If the key is not present in the environment, the app will not start */ this.publicKey = this.configService.get('IMAGEKIT_PUBLIC_KEY')!; this.client = new ImageKit({ privateKey: this.configService.get('IMAGEKIT_PRIVATE_KEY')!, }); } generateSignedURLs<ImageKitSignedUrlsResult>(urlCount: number){ // url generation code... return { urls, pubKey: this.publicKey } as ImageKitSignedUrlsResult; }}
proxy service
This is the service which every other service can use to upload files. and they dont need to know which service is being used. We can register the providers in constructor through a Map(). Then we can activate the service in the onModuleInit() by the name we had used earlier to register. This can be made dynamic using environment varibale as well, but I wanted it to be selected manually as per my wish.
Injectable()export class FileUploaderService implements IFileUploader, OnModuleInit { private uploaderRegistry = new Map<UploadProviderName, any>(); private activeUploader!: IFileUploader; constructor(private readonly moduleRef: ModuleRef) { this.uploaderRegistry.set('imagekit', ImageKitUploader); } onModuleInit() { // You can change the provider as per your choice here const uploaderName: UploadProviderName = 'imagekit'; const uploader = this.uploaderRegistry.get(uploaderName); if (!uploader) { throw new Error(`Upload provider [${uploaderName}] is not registered.`); } this.activeUploader = this.moduleRef.get(uploader, { strict: false }); } generateSignedURLs<T>(urlCount: number = 1): T { return this.activeUploader.generateSignedURLs(urlCount); }}
usage:
This way I can use it in any service I want just by injecting it.
Injectable()export class ProductsService { constructor( private readonly uploaderService: FileUploaderService ) {} generateProductImgUploadURLs(data: ProductURLDTO) { const urlsData = this.uploaderService.generateSignedURLs(data.count); return urlsData; }}
I have done it for only one requirement. Similarly, it can be done for payment service, otp service, email service, etc.
Leave a comment