如果您正在使用NestJS,则可能比您想象的要简单。
文件上传模块:
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { S3 } from 'aws-sdk';
import { FileUploadController } from './file-upload.controller';
import { FileUploadRepository } from './file-upload.repository';
import { FileUploadService } from './file-upload.service';
@Module({
imports: [TypeOrmModule.forFeature([FileUploadRepository])],
controllers: [FileUploadController],
providers: [
FileUploadService,
{
provide: S3,
useFactory: (configService: ConfigService) =>
new S3({
accessKeyId: configService.get('AWS_ACCESS_KEY'),
secretAccessKey: configService.get('AWS_ACCESS_SECRET'),
region: configService.get('AWS_REGION'),
}),
},
],
})
export class FileUploadModule {}
服务本身:
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { S3 } from 'aws-sdk';
import { v4 as uuid } from 'uuid';
import { FileUpload } from './file-upload.entity';
import { FileUploadRepository } from './file-upload.repository';
@Injectable()
export class FileUploadService {
constructor(
@InjectRepository(FileUploadRepository)
private readonly publicFileRepository: FileUploadRepository,
private readonly configService: ConfigService,
private readonly s3: S3,
) {}
async uploadPublicFile(
dataBuffer: Buffer,
filename: string,
): Promise<FileUpload> {
const uploadResult = await this.s3
.upload({
Bucket: this.configService.get('AWS_BUCKET_NAME'),
Body: dataBuffer,
Key: `${uuid()}-${filename}`,
})
.promise();
const createdFile = this.publicFileRepository.create({
key: uploadResult.Key,
url: uploadResult.Location,
});
await this.publicFileRepository.save(createdFile);
return createdFile;
}
async deletePublicFile(
publicFileId: string,
publicFileKey: string,
): Promise<FileUpload> {
const response = await this.s3
.deleteObject({
Bucket: this.configService.get('AWS_BUCKET_NAME'),
Key: publicFileKey,
})
.promise();
if (!response) {
throw new InternalServerErrorException(
`Could not delete file ${publicFileKey}`,
);
}
const { raw: deletedItem } = await this.publicFileRepository.delete(
publicFileId,
);
return deletedItem;
}
}
最后进行测试:
import { InternalServerErrorException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { S3 } from 'aws-sdk';
import { mockFileUpload } from './file-upload.mock';
import { FileUploadRepository } from './file-upload.repository';
import { FileUploadService } from './file-upload.service';
export const mockFileUploadRepository = () => ({
create: jest.fn(),
save: jest.fn(),
delete: jest.fn(),
});
const mS3Instance = {
upload: jest.fn().mockReturnThis(),
promise: jest.fn(),
deleteObject: jest.fn().mockReturnThis(),
};
describe('FileUploadService', () => {
let service: FileUploadService;
let repository;
let s3Service;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
FileUploadService,
{
provide: FileUploadRepository,
useFactory: mockFileUploadRepository,
},
{
provide: ConfigService,
useValue: {
get: jest.fn(),
},
},
{
provide: S3,
useFactory: () => mS3Instance,
},
],
}).compile();
service = module.get<FileUploadService>(FileUploadService);
repository = module.get<FileUploadRepository>(FileUploadRepository);
repository = module.get<FileUploadRepository>(FileUploadRepository);
s3Service = module.get<S3>(S3);
});
describe('FileUploadService.uploadPublicFile', () => {
it('should create public file and throw no error', async () => {
repository.create.mockResolvedValue(mockFileUpload);
repository.save.mockResolvedValue(mockFileUpload);
s3Service.promise = jest.fn().mockResolvedValue({
Key: 'some-key',
Location: 'some-location',
});
const file = Buffer.alloc(513, '0');
const response = await service.uploadPublicFile(file, 'somefilename');
expect(response).toBeDefined();
});
});
describe('FileUploadService.deletePublicFile', () => {
it('should delete public file and throw no error', async () => {
repository.delete.mockResolvedValue({ raw: mockFileUpload });
repository.save.mockResolvedValue(mockFileUpload);
s3Service.promise = jest.fn().mockResolvedValue({
Key: 'some-key',
Location: 'some-location',
});
const response = await service.deletePublicFile('someid', 'somefilename');
expect(response).toBeDefined();
});
it('should not delete public file and throw InternalServerErrorException', async () => {
repository.delete.mockResolvedValue({ raw: mockFileUpload });
repository.save.mockResolvedValue(mockFileUpload);
s3Service.promise = jest.fn().mockResolvedValue(null);
const promise = service.deletePublicFile('someid', 'somefilename');
expect(promise).rejects.toThrow(InternalServerErrorException);
});
});
});
jest.fn().mockReturnThis()
是什么意思?它是否允许您链接调用? - cischajest.mock()
的模块工厂不允许引用任何超出范围的变量。 无效的变量访问:mS3Instance 允许的对象:Array、ArrayBuffer、Atomics、BigInt、BigInt64Array、BigUint64Array、Boolean、Buffer、DTRACE_HTTP_CLIENT_REQUEST、DTRACE_HTTP_CLIENT_RESPONSE、DTRACE_HTTP_SERVER_REQUEST、DTRACE_HTTP_SERVER_RESPONSE、DTRACE_NET_SERVER_CONNECTION、DTRACE_NET_STREAM_END、DataView、Date、Error、EvalError、... - Benjamin HeinkemS3Instance
重命名为以mock
开头的名称,例如mockS3
。 - timomeinenconst mockS3Instance = { putObject: jest.fn(), } console.log(mockS3Instance) jest.mock('aws-sdk', () => { return { S3: jest.fn(() => mockS3Instance) } });
,但是在这一行代码return { S3: jest.fn(() => mockS3Instance) }
上出现了错误ReferenceError: Cannot access 'mockS3Instance' before initialization
。 - Abhilash D K