Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 3m0s
- 安装 cos-nodejs-sdk-v5,封装 CosStorageProvider(upload/download/delete/healthCheck)
- 重写 StorageService,新增 createUploadUrl/verifyUpload/getDownloadUrl/deleteObject
- 创建 FilesModule:POST /files/upload-url, POST /files/complete, GET /files/:id, DELETE /files/:id
- UploadedFile 新增 objectKey/bucket 字段
- 对象键格式 {userId}/{YYYYMM}/{sanitizedName}.{ext}
- 接入文件类型校验(ALLOWED_FILE_TYPES)+ 上传限流(10次/小时/用户)
- 配置文件 cos.longde.cloud → zhixi-1259685406 / ap-guangzhou
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import {
|
|
Injectable,
|
|
NotFoundException,
|
|
ForbiddenException,
|
|
} from '@nestjs/common';
|
|
import { FilesRepository } from './files.repository';
|
|
import { StorageService } from '../../infrastructure/storage/storage.service';
|
|
import { CosStorageProvider } from '../../infrastructure/storage/cos-storage.provider';
|
|
import { CreateUploadUrlDto, CompleteUploadDto } from './dto';
|
|
|
|
@Injectable()
|
|
export class FilesService {
|
|
constructor(
|
|
private readonly repository: FilesRepository,
|
|
private readonly storage: StorageService,
|
|
private readonly cos: CosStorageProvider,
|
|
) {}
|
|
|
|
async requestUploadUrl(userId: string, dto: CreateUploadUrlDto) {
|
|
return this.storage.createUploadUrl(userId, {
|
|
filename: dto.filename,
|
|
mimeType: dto.mimeType,
|
|
sizeBytes: dto.sizeBytes,
|
|
});
|
|
}
|
|
|
|
async confirmUpload(userId: string, dto: CompleteUploadDto) {
|
|
const info = await this.storage.verifyUpload(dto.objectKey);
|
|
|
|
const parts = dto.objectKey.split('/');
|
|
const originalFilename = parts[parts.length - 1];
|
|
|
|
return this.repository.create({
|
|
userId,
|
|
filename: originalFilename,
|
|
mimeType: info.contentType,
|
|
storagePath: dto.objectKey,
|
|
objectKey: dto.objectKey,
|
|
bucket: this.cos.getBucket(),
|
|
sizeBytes: info.size,
|
|
checksum: dto.checksum,
|
|
});
|
|
}
|
|
|
|
async getFile(userId: string, fileId: string) {
|
|
const file = await this.repository.findById(fileId);
|
|
if (!file) {
|
|
throw new NotFoundException('文件不存在');
|
|
}
|
|
if (file.userId !== userId) {
|
|
throw new ForbiddenException('无权访问该文件');
|
|
}
|
|
|
|
const downloadUrl = await this.storage.getDownloadUrl(file.objectKey!);
|
|
return { file, downloadUrl };
|
|
}
|
|
|
|
async deleteFile(userId: string, fileId: string) {
|
|
const file = await this.repository.findById(fileId);
|
|
if (!file) {
|
|
throw new NotFoundException('文件不存在');
|
|
}
|
|
if (file.userId !== userId) {
|
|
throw new ForbiddenException('无权操作该文件');
|
|
}
|
|
|
|
await this.storage.deleteObject(file.objectKey!);
|
|
await this.repository.delete(fileId);
|
|
}
|
|
|
|
async findByUserId(userId: string) {
|
|
return this.repository.findByUserId(userId);
|
|
}
|
|
}
|