From e4b77eb2f63fbdda3569aa8c9dcea63dc4109128 Mon Sep 17 00:00:00 2001 From: Carlos Santos <4a.santos@gmail.com> Date: Thu, 29 May 2025 13:57:31 +0200 Subject: [PATCH] backend: add getObjectHeaders method to StorageProvider and implement in S3StorageProvider and MeetStorageService --- backend/src/services/recording.service.ts | 3 +-- .../storage/providers/s3-storage.provider.ts | 20 +++++++++++++++++++ .../src/services/storage/storage.interface.ts | 9 ++++++++- .../src/services/storage/storage.service.ts | 12 ++++++++++- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/backend/src/services/recording.service.ts b/backend/src/services/recording.service.ts index 55f83d6..3e29520 100644 --- a/backend/src/services/recording.service.ts +++ b/backend/src/services/recording.service.ts @@ -395,8 +395,7 @@ export class RecordingService { if (!recordingPath) throw new Error(`Error extracting path from recording ${recordingId}`); - const data = await this.s3Service.getHeaderObject(recordingPath); - const fileSize = data.ContentLength; + const { contentLength: fileSize } = await this.storageService.getObjectHeaders(recordingPath); if (!fileSize) { this.logger.error(`Error getting file size for recording ${recordingId}`); diff --git a/backend/src/services/storage/providers/s3-storage.provider.ts b/backend/src/services/storage/providers/s3-storage.provider.ts index b988bd9..5fa62eb 100644 --- a/backend/src/services/storage/providers/s3-storage.provider.ts +++ b/backend/src/services/storage/providers/s3-storage.provider.ts @@ -37,6 +37,26 @@ export class S3StorageProvider< @inject(RedisService) protected redisService: RedisService ) {} + /** + * Retrieves metadata headers for an object stored in S3. + * + * @param filePath - The path/key of the file in the S3 bucket + * @returns A promise that resolves to an object containing the content length and content type of the file + * @throws Will throw an error if the S3 operation fails or the file doesn't exist + */ + async getObjectHeaders(filePath: string): Promise<{ contentLength?: number; contentType?: string }> { + try { + const data = await this.s3Service.getHeaderObject(filePath); + return { + contentLength: data.ContentLength, + contentType: data.ContentType + }; + } catch (error) { + this.logger.error(`Error fetching object headers for ${filePath}: ${error}`); + throw error; + } + } + /** * Initializes global preferences. If no preferences exist, persists the provided defaults. * If preferences exist but belong to a different project, they are replaced. diff --git a/backend/src/services/storage/storage.interface.ts b/backend/src/services/storage/storage.interface.ts index dfe8377..0e4c373 100644 --- a/backend/src/services/storage/storage.interface.ts +++ b/backend/src/services/storage/storage.interface.ts @@ -24,6 +24,14 @@ export interface StorageProvider< */ initialize(defaultPreferences: GPrefs): Promise; + /** + * Retrives the headers of an object stored in the storage provider. + * This is useful to get the content length and content type of the object without downloading it. + * + * @param filePath - The path of the file to retrieve headers for. + */ + getObjectHeaders(filePath: string): Promise<{ contentLength?: number; contentType?: string }>; + /** * Retrieves the global preferences of Openvidu Meet. * @@ -148,5 +156,4 @@ export interface StorageProvider< * @returns A promise that resolves when the recording binary files have been deleted. */ deleteRecordingBinaryFilesByPaths(recordingPaths: string[]): Promise; - } diff --git a/backend/src/services/storage/storage.service.ts b/backend/src/services/storage/storage.service.ts index 3482f66..9c2c40e 100644 --- a/backend/src/services/storage/storage.service.ts +++ b/backend/src/services/storage/storage.service.ts @@ -30,6 +30,17 @@ export class MeetStorageService< this.storageProvider = this.storageFactory.create(); } + async getObjectHeaders(filePath: string): Promise<{ contentLength?: number; contentType?: string }> { + try { + const headers = await this.storageProvider.getObjectHeaders(filePath); + this.logger.verbose(`Object headers retrieved: ${JSON.stringify(headers)}`); + return headers; + } catch (error) { + this.handleError(error, 'Error retrieving object headers'); + throw internalError('Getting object headers'); + } + } + /** * Initializes default preferences if not already initialized. * @returns {Promise} Default global preferences. @@ -232,7 +243,6 @@ export class MeetStorageService< return this.storageProvider.deleteRecordingBinaryFilesByPaths(recordingPaths); } - /** * Returns the default global preferences. * @returns {GPrefs}