本系列教程将教你使用 NestJS 构建一个生产级别的 REST API 风格的权限管理后台服务【代码仓库地址】。
【在线预览地址】账号:test,密码:d.12345
本章节内容: 1. 使用 Docker 运行 Minio 服务; 2. 设置 Minio; 3. 在 NestJS 中引入 Minio 服务与预签名接口。
1. 使用 Docker 运行 Minio 服务
打开 docker-compose.yml 文件,添加以下内容:
...
minio:
image: bitnami/minio:latest
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_data:/data
environment:
MINIO_ROOT_USER: wansongtao # 管理后台登录用户账号
MINIO_ROOT_PASSWORD: w.12345.st # 管理后台登录用户密码
MINIO_DEFAULT_BUCKETS: avatar # 新建一个桶,名称为 avatar
volumes:
...
minio_data:
重新运行 docker-compose --env-file .env.development up --build 命令构建容器。
然后,在浏览器中打开 http://localhost:9001/ 就可以看到 Minio 的管理界面了。
2. 设置 Minio
首先输入我们在 docker-compose.yml 中设置的账号密码,登录进入 Minio 的管理后台网站。
然后我们需要添加一个 Access Keys ,后续通过 NestJS 访问这个服务时需要。先点击左边菜单栏的 Access Keys,再点击右边的 Create access key 按钮。 如图所示。
保存好
Access Key 与 Secret Key,后续需要用到。
最后还需要调整一下桶的访问策略,按下图所示操作:
3. 在 NestJS 中引入 Minio 服务
3.1 添加环境变量配置
首先,在 .env.development 中添加相关环境变量配置:
# Minio
MINIO_END_POINT="localhost"
MINIO_PORT=9000
MINIO_USE_SSL=false
# 填入之前在 Minio 后台创建的 key
MINIO_ACCESS_KEY="..."
MINIO_SECRET_KEY="..."
MINIO_BUCKET_NAME="avatar"
MINIO_EXPIRES_IN=120
其次,在 /src/common/config/index.ts 中添加相应的常量:
minio: {
endPoint: configService.get<string>('MINIO_END_POINT'),
port: +configService.get<number>('MINIO_PORT'),
useSSL: configService.get('MINIO_USE_SSL') === 'true',
accessKey: configService.get<string>('MINIO_ACCESS_KEY'),
secretKey: configService.get<string>('MINIO_SECRET_KEY'),
bucketName: configService.get<string>('MINIO_BUCKET_NAME'),
expiresIn: +configService.get<number>('MINIO_EXPIRES_IN'),
},
3.2 新建 Upload 模块
接下来在项目根目录新开一个终端窗口,输入 nest g res upload 命令,新建一个 upload 模块。
然后安装 Minio 库,输入 pnpm i minio。
最后,打开 /src/upload/upload.service.ts 文件添加以下代码:
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { getBaseConfig } from 'src/common/config';
import { Client } from 'minio';
@Injectable()
export class UploadService {
private minioClient: Client;
constructor(private readonly configService: ConfigService) {
const config = getBaseConfig(configService);
this.minioClient = new Client({
endPoint: config.minio.endPoint,
port: config.minio.port,
useSSL: config.minio.useSSL,
accessKey: config.minio.accessKey,
secretKey: config.minio.secretKey,
});
}
}
启动开发环境,没有报错则代表连上 Minio 服务了。
注意:要先运行相关 docker 服务哦。
3.3 添加预签名接口
首先,打开 /src/upload/upload.service.ts 文件添加一个预签名方法:
async presigned(fileName: string) {
const config = getBaseConfig(this.configService);
const url = await this.minioClient.presignedPutObject(
config.minio.bucketName,
fileName,
config.minio.expiresIn,
);
return { presignedUrl: url };
}
使用 minio 提供的 presignedPutObject 预签名方法,传入桶名、文件名与过期时间即可生成一个上传链接。
其次,新建一个 /src/upload/dto/presigned.dto.ts 文件并添加以下内容:
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';
export class PresignedDto {
@IsString({ message: '文件名必须是字符串' })
@IsNotEmpty({ message: '文件名不能为空' })
@ApiProperty({ description: '文件名' })
filename: string;
}
该 DTO 将用来校验接口参数与用作 Swagger 文档中该接口参数的类型说明。
然后,新建一个 /src/upload/entities/presigned.entity.ts 文件并添加以下内容:
import { ApiProperty } from '@nestjs/swagger';
export class PresignedEntity {
@ApiProperty({
description: '预签名 URL',
})
presignedUrl: string;
}
该实体将用作接口返回对象与 Swagger 文档中该接口响应数据的类型说明。
最后,修改 /src/upload/upload.controller.ts 文件的内容为:
import { Body, Controller, Post } from '@nestjs/common';
import { UploadService } from './upload.service';
import { ApiOperation } from '@nestjs/swagger';
import { ApiBaseResponse } from 'src/common/decorator/api-base-response.decorator';
import { PresignedDto } from './dto/presigned.dto';
import { PresignedEntity } from './entities/presigned.entity';
@ApiTags('upload')
@Controller('upload')
export class UploadController {
constructor(private readonly uploadService: UploadService) {}
@ApiOperation({ summary: '获取预签名 URL' })
@ApiBaseResponse(PresignedEntity)
@Post('presigned')
presigned(@Body() presignedDto: PresignedDto): Promise<PresignedEntity> {
return this.uploadService.presigned(presignedDto.filename);
}
}
@ApiTags('upload') 装饰器用来给整个控制器添加 upload 标签,方便对 Swagger API 文档中的接口进行分组管理。
@ApiOperation() 装饰器用来为 Swagger API 文档中该接口添加说明信息。
@ApiBaseResponse() 是我们之前封装的一个装饰器,是用来:
- 定义 API 端点的成功响应;
- 为 Swagger API 文档提供该接口响应数据的类型说明。
@Post('presigned') 装饰器用来声明 POST 类型的接口,这里还设置了该接口的地址为 presigned 。
@Body() 用来获取接口请求体中的参数数据。 presignedDto: PresignedDto 表示指定使用 PresignedDto 对象校验参数格式。
至此,文件预签名接口就开发完成了。运行开发环境后,可以用 postman 之类的工具测试该接口是否能返回预签名 URL,然后再测试能否通过预签名 URL 将图片文件上传到 minio。
接口的单元测试代码请查看仓库,下一章节见~