import { Injectable, BadRequestException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { OssService } from '../oss/oss.service'; import * as fs from 'fs'; import * as path from 'path'; import { randomBytes } from 'crypto'; @Injectable() export class UploadService { private readonly uploadDir: string; private readonly useOss: boolean; constructor( private configService: ConfigService, private ossService: OssService, ) { // 本地上传文件存储目录(作为 COS 的备用方案) this.uploadDir = path.join(process.cwd(), 'uploads'); // 确保本地上传目录存在 if (!fs.existsSync(this.uploadDir)) { fs.mkdirSync(this.uploadDir, { recursive: true }); } // 检查是否使用 COS this.useOss = this.ossService.isEnabled(); if (this.useOss) { console.log('文件上传将使用腾讯云 COS'); } else { console.log('文件上传将使用本地存储,目录:', this.uploadDir); } } async uploadFile( file: Express.Multer.File, tenantId?: number, userId?: number, ): Promise<{ url: string; fileName: string; size: number }> { if (!file) { throw new BadRequestException('文件不存在'); } // 优先使用 COS if (this.useOss) { return this.uploadToOss(file, tenantId, userId); } // 备用方案:本地存储 return this.uploadToLocal(file, tenantId, userId); } /** * 上传到腾讯云 COS */ private async uploadToOss( file: Express.Multer.File, tenantId?: number, userId?: number, ): Promise<{ url: string; fileName: string; size: number }> { try { const result = await this.ossService.uploadFile( file.buffer, file.originalname, tenantId, userId, ); return { url: result.url, fileName: result.fileName, size: file.size, }; } catch (error: any) { console.error('COS 上传失败,尝试本地存储:', error.message); // COS 失败时回退到本地存储 return this.uploadToLocal(file, tenantId, userId); } } /** * 上传到本地存储 */ private async uploadToLocal( file: Express.Multer.File, tenantId?: number, userId?: number, ): Promise<{ url: string; fileName: string; size: number }> { // 生成唯一文件名 const fileExt = path.extname(file.originalname); const uniqueId = randomBytes(16).toString('hex'); const fileName = `${uniqueId}${fileExt}`; // 根据租户ID和用户ID创建目录结构:uploads/tenantId/userId/ let targetDir = this.uploadDir; if (tenantId) { targetDir = path.join(targetDir, `tenant_${tenantId}`); if (!fs.existsSync(targetDir)) { fs.mkdirSync(targetDir, { recursive: true }); } if (userId) { targetDir = path.join(targetDir, `user_${userId}`); if (!fs.existsSync(targetDir)) { fs.mkdirSync(targetDir, { recursive: true }); } } } // 文件保存路径 const filePath = path.join(targetDir, fileName); // 保存文件 fs.writeFileSync(filePath, file.buffer); // 生成访问URL // 格式:/api/uploads/tenantId/userId/fileName 或 /api/uploads/fileName let urlPath = '/api/uploads'; if (tenantId) { urlPath += `/tenant_${tenantId}`; if (userId) { urlPath += `/user_${userId}`; } } urlPath += `/${fileName}`; return { url: urlPath, fileName: file.originalname, size: file.size, }; } }