import COS from 'cos-nodejs-sdk-v5'; import OSS from 'ali-oss'; import fs from 'fs'; import path from 'path'; class StorageManager { constructor(config) { this.config = config; this.storageType = (config.STORAGE_TYPE || 'local').toLowerCase(); // 初始化COS客户端 if (this.storageType === 'cos' && config.COS_SECRET_ID && config.COS_SECRET_KEY) { const cosConfig = { SecretId: config.COS_SECRET_ID, SecretKey: config.COS_SECRET_KEY, }; this.cosClient = new COS(cosConfig); } // 初始化OSS客户端 if (this.storageType === 'oss') { const ossConfig = { region: config.OSS_REGION, accessKeyId: config.OSS_ACCESS_KEY_ID, accessKeySecret: config.OSS_ACCESS_KEY_SECRET, bucket: config.OSS_BUCKET }; if (ossConfig.accessKeyId && ossConfig.accessKeySecret && ossConfig.region && ossConfig.bucket) { this.ossClient = new OSS(ossConfig); } } } /** * 上传文件 * @param basePath 应用根路径 * @param upload_path 上传路径 * @param filename 文件名 * @returns {Promise<*>} */ async upload(basePath, upload_path, filename) { switch (this.storageType.toLowerCase()) { case 'oss': return this.uploadToOSS(basePath, upload_path, filename); case 'cos': return this.uploadToCOS(basePath, upload_path, filename); case 'local': return this.uploadToLocal(basePath, upload_path, filename); default: return this.uploadToCOS(basePath, upload_path, filename); } } /** * 上传文件到本地 * @param basePath 应用根路径 * @param upload_path 上传路径 * @param filename 文件名 * @returns {Promise<*>} */ async uploadToLocal(basePath, upload_path, filename) { const date = new Date(); const pathDir = `${upload_path}/${date.getFullYear()}${date.getMonth() + 1}/${date.getDate()}`; const localFilePath = `${basePath}/${upload_path}/${filename}`; const targetDir = `${basePath}/${pathDir}`; // 确保目标目录存在 await fs.promises.mkdir(targetDir, { recursive: true }); // 移动文件到目标位置 const targetPath = `${targetDir}/${filename}`; await fs.promises.rename(localFilePath, targetPath); // 构造访问URL const localDomain = process.env.LOCAL_DOMAIN || 'http://localhost:3000'; const url = `${localDomain}/${pathDir}/${filename}`; return { name: filename, path: url }; } /** * 上传文件到COS * @param basePath 应用根路径 * @param upload_path 上传路径 * @param filename 文件名 * @returns {Promise<*>} */ async uploadToCOS(basePath, upload_path, filename) { const date = new Date(); const path = `${upload_path}/${date.getFullYear()}${date.getMonth() + 1}/${date.getDate()}`; const localFilePath = `${basePath}/${upload_path}/${filename}`; const cosDomain = this.config.COS_DOMAIN; // 保存this.config的引用 return new Promise((resolve, reject) => { this.cosClient.putObject({ Bucket: this.config.COS_BUCKET, Region: this.config.COS_REGION, Key: `${path}/${filename}`, StorageClass: 'STANDARD', Body: fs.createReadStream(localFilePath), }, function (err, data) { fs.unlink(localFilePath, function (err) { if (err) { console.error('Failed to delete local file:', err); } }); if (err) { reject(err); } else { const response = { name: filename, path: `${cosDomain}/${path}/${filename}` }; resolve(response); } }); }); } /** * 上传文件到OSS * @param basePath 应用根路径 * @param upload_path 上传路径 * @param filename 文件名 * @returns {Promise<*>} */ async uploadToOSS(basePath, upload_path, filename) { if (!this.ossClient) { throw new Error('OSS client not initialized. Please check OSS configuration.'); } const date = new Date(); const path = `${upload_path}/${date.getFullYear()}${date.getMonth() + 1}/${date.getDate()}`; const fullPath = `${path}/${filename}`; const localFilePath = `${basePath}/${upload_path}/${filename}`; try { const result = await this.ossClient.put(fullPath, localFilePath); // 上传完成后删除本地文件 fs.unlink(localFilePath, function (err) { if (err) { console.error('Failed to delete local file:', err); } }); return { name: filename, path: result.url }; } catch (err) { // 出错时也删除本地文件 fs.unlink(localFilePath, function (err) { if (err) { console.error('Failed to delete local file:', err); } }); throw err; } } } export default StorageManager;