@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
JavaScript
// 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;