NestJS 项目实战-权限管理系统开发(六)

本章节内容: 1. 使用 Docker 运行 Minio 服务; 2. 设置 Minio; 3. 在 NestJS 中引入 Minio 服务与预签名接口。

本系列教程将教你使用 NestJS 构建一个生产级别的 REST API 风格的权限管理后台服务。

该教程主要包含以下内容:

  1. 用户登录,包含身份验证、无感刷新 token、单点登录;
  2. 用户、角色、权限的增删改查;
  3. 接口级别的权限控制,使用装饰器与守卫实现;
  4. 接口返回数据格式统一,使用拦截器与异常过滤器实现;
  5. 使用 Winston 进行日志管理;
  6. Minio 的使用,包含文件上传预签名等;
  7. 编写 Swagger API 文档;
  8. 数据库设计与 Prisma 建模
  9. 单元测试;
  10. 生产环境部署,使用 Docker

主要技术栈:NestJS、TS、PostgreSQL、Prisma、Redis、Minio、Winston、Docker。

代码仓库在线预览地址,账号:test,密码:d.12345

1. 使用 Docker 运行 Minio 服务

打开 docker-compose.yml 文件,添加以下内容:

ts 复制代码
...

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
      
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 KeySecret Key,后续需要用到。

最后还需要调整一下桶的访问策略,按下图所示操作:

3. 在 NestJS 中引入 Minio 服务

3.1 添加环境变量配置

首先,在 .env.development 中添加相关环境变量配置:

env 复制代码
# Minio
MINIO_END_POINT="localhost"
MINIO_PORT=9000
MINIO_USE_SSL=false
MINIO_ACCESS_KEY="..."
MINIO_SECRET_KEY="..."
MINIO_BUCKET_NAME="avatar"
MINIO_EXPIRES_IN=120

其次,在 /src/common/config/index.ts 中添加相应的常量:

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 文件添加以下代码:

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 文件添加一个预签名方法:

ts 复制代码
  async presignedUrl(fileName: string) {
    const config = getBaseConfig(this.configService);

    const url = await this.minioClient.presignedPutObject(
      config.minio.bucketName,
      fileName,
      config.minio.expiresIn,
    );

    return { presignedUrl: url };
  }

该方法接收一个文件名参数,返回一个上传文件链接。

其次,新建一个 /src/upload/dto/presigned.dto.ts 文件并添加以下内容:

ts 复制代码
import { ApiProperty } from '@nestjs/swagger';
import { IsNotEmpty, IsString } from 'class-validator';

export class PresignedDto {
  @IsString({ message: '文件名必须是字符串' })
  @IsNotEmpty({ message: '文件名不能为空' })
  @ApiProperty({ description: '文件名' })
  filename: string;
}

DTO 将用来校验接口参数。

然后,新建一个 /src/upload/entities/presigned.entity.ts 文件并添加以下内容:

ts 复制代码
import { ApiProperty } from '@nestjs/swagger';

export class PresignedEntity {
  @ApiProperty({
    description: '预签名 URL',
  })
  presignedUrl: string;
}

该实体将用作接口返回对象。

最后,修改 /src/upload/upload.controller.ts 文件的内容为:

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';

@Controller('upload')
export class UploadController {
  constructor(private readonly uploadService: UploadService) {}

  @ApiOperation({ summary: '获取预签名 URL' })
  @ApiBaseResponse(PresignedEntity)
  @Post('presigned')
  presignedUrl(@Body() presignedDto: PresignedDto): Promise<PresignedEntity> {
    return this.uploadService.presignedUrl(presignedDto.filename);
  }
}

至此,文件预签名接口就开发完成了。运行开发环境后,可以用 postman 之类的工具测试该接口是否能返回预签名 URL,然后再测试能否通过预签名 URL 将图片文件上传到 minio。

接口的单元测试代码请查看仓库

相关推荐
吃海鲜的骆驼7 分钟前
服务异步通讯与RabbitMQ
java·分布式·后端·rabbitmq
m0_748233649 分钟前
RabbitMQ 进阶
android·前端·后端
m0_7482386318 分钟前
Spring Boot项目接收前端参数的11种方式
前端·spring boot·后端
桦说编程20 分钟前
【硬核总结】如何轻松实现只计算一次、惰性求值?良性竞争条件的广泛使用可能超过你的想象!String实际上是可变的?
后端·函数式编程
嘵奇25 分钟前
Node.js二:第一个Node.js应用
node.js
菜鸟阿达1 小时前
spring boot 2.7 + seata +微服务 降级失败问题修复
spring boot·后端·微服务
清河__2 小时前
【Go】十七、grpc 服务的具体功能编写
开发语言·后端·golang
张声录12 小时前
国密算法Sm2工具类--golang实现版
开发语言·后端·golang
m0_748252389 小时前
Node.js HTTP模块详解:创建服务器、响应请求与客户端请求
服务器·http·node.js
一小路一10 小时前
从0-1学习Mysql第五章: 索引与优化
数据库·后端·学习·mysql·面试