UNPKG

@b2y/document-module

Version:

A flexible multi-provider storage adapter for file operations across S3, Azure Blob, Google Drive, and local storage

138 lines (126 loc) 4.48 kB
// src/FileUtilityService.js const fs = require('fs-extra'); const path = require('path'); const StorageFactory = require('./StorageFactory'); const logger = require('../logger'); class FileUtilityService { /** * Create a new FileUtilityService * * @param {object} config - Configuration options */ constructor(config = {}) { // Initialize with default provider, but allow changing it dynamically this.storageType = config.storageType || process.env.DOCUMENT_STORAGE_TYPE; this.providerConfig = config.providerConfig || {}; this.storageProvider = StorageFactory.createStorageProvider(this.storageType, this.providerConfig); } /** * Set storage provider dynamically * * @param {string} storageType - Type of storage provider to use * @param {object} config - Configuration for the provider * @returns {boolean} - Success status */ setStorageProvider(storageType, config = {}) { try { this.storageProvider = StorageFactory.createStorageProvider(storageType, config); this.storageType = storageType; this.providerConfig = config; return true; } catch (error) { logger.error(`Failed to set storage provider to ${storageType}:`, error); return false; } } /** * Upload a file to storage * * @param {object|string|Buffer} file - File to upload * @param {string} destination - Destination path/directory * @returns {Promise<object>} - Upload result */ async uploadFile(file, destination) { try { // Generate a unique filename if originalname exists let filename; if (file.originalname) { const ext = path.extname(file.originalname); const timestamp = Date.now(); filename = `${path.basename(file.originalname, ext).replace(/[^\w-]/g, '_')}_${timestamp}${ext}`; } else { // For buffers or streams without a name, generate one const timestamp = Date.now(); const randomStr = Math.random().toString(36).substring(2, 8); filename = `file_${timestamp}_${randomStr}`; } // Create the full destination path const destinationPath = `${destination}/${filename}`; // Upload using storage provider const result = await this.storageProvider.uploadFile(file, destinationPath); // If there was a temporary file, clean it up if (file.path) { try { await fs.unlink(file.path); } catch (err) { logger.warn(`Failed to delete temporary file: ${file.path}`, err); } } return result; } catch (error) { logger.error('Error in FileUtilityService.uploadFile:', error); throw error; } } /** * Get a file from storage * * @param {string} filePath - Path to the file * @returns {Promise<Stream|Buffer>} - File data */ async getFile(filePath) { try { return await this.storageProvider.getFile(filePath); } catch (error) { logger.error('Error in FileUtilityService.getFile:', error); throw error; } } /** * Get a public URL for a file * * @param {string} filePath - Path to the file * @param {string} storageType - Optional storage type to use * @param {object} config - Configuration for the provider * @returns {Promise<string>} - Public URL */ async getPublicUrl(filePath, storageType = null, config = {}) { try { // If storageType is provided, temporarily use that provider for this operation if (storageType) { const tempProvider = StorageFactory.createStorageProvider(storageType, config); return await tempProvider.getPublicUrl(filePath); } // Otherwise use the current provider return await this.storageProvider.getPublicUrl(filePath); } catch (error) { logger.error('Error in FileUtilityService.getPublicUrl:', error); throw error; } } /** * Delete a file from storage * * @param {string} filePath - Path to the file * @returns {Promise<object>} - Delete result */ async deleteFile(filePath) { try { return await this.storageProvider.deleteFile(filePath); } catch (error) { logger.error('Error in FileUtilityService.deleteFile:', error); throw error; } } } module.exports = FileUtilityService;