在 nest 中集成 minio,阿里云能做的我们也能做 🐒🐒🐒

发布于:2024-04-28 ⋅ 阅读:(26) ⋅ 点赞:(0)

文章开始之前分享两个开源项目,会一直维护的,欢迎 star,如果你感兴趣或者想参与学习,可以加我微信 yunmz777,最近也在找工作 ing,欢迎内推......

浪费你两秒钟时间,我们正文开始!!!

最近在做毕业项目,而且毕业项目用的是公司的项目,因为已经离职了,而且公司的 oss 服务也到期了,所以就导致了之前写的一些东西无法运行,但是又不想花太多时间去配置一些阿里云 oss 的服务之类的,于是便使用 minio 自己搭了一个。

这篇文章中我们已经讲解到了如何使用 docker-compose 来搭建一个 minio 服务了,如果不懂的话可以到这里进行阅读:

接下来的内容中我们将讲解如何在 nestjs 中集成 minio 服务。

配置 minio

首先我们先配置一下我们的基本服务,因为我们设置了环境变量,那么我们可以把我们的账号和密码配置在环境变量上面:

MINIO_ENDPOINT= localhost
MINIO_PORT= 9000
MINIO_ACCESS_KEY= moment
MINIO_SECRET_KEY= moment666

环境变量配置完成之后,我们配置一个 ts 枚举类型来:

export enum MiNiOConfigEnum {
  MINIO_ENDPOINT = "MINIO_ENDPOINT",
  MINIO_PORT = "MINIO_PORT",
  MINIO_ACCESS_KEY = "MINIO_ACCESS_KEY",
  MINIO_SECRET_KEY = "MINIO_SECRET_KEY",
}

在上面键和值都是相同的,这样就能对应到我们环境变量中设置的值了,这个时候我们就可以添加我们的 minio 配置了:

import { ConfigService } from "@nestjs/config";

import { MiNiOConfigEnum } from "@/common/enum/config.enum";

interface MiNiOConfig {
  endPoint: string;
  port: number;
  useSSL: boolean;
  accessKey: string;
  secretKey: string;
}

export default function loadMiNiOConfig(
  configService: ConfigService
): MiNiOConfig {
  const { MINIO_ACCESS_KEY, MINIO_ENDPOINT, MINIO_PORT, MINIO_SECRET_KEY } =
    MiNiOConfigEnum;

  return {
    endPoint: configService.get(MINIO_ENDPOINT),
    port: parseInt(configService.get(MINIO_PORT), 10),
    useSSL: false,
    accessKey: configService.get(MINIO_ACCESS_KEY),
    secretKey: configService.get(MINIO_SECRET_KEY),
  };
}

这里返回了一个对象,这里都是我们连接 minio 要用到的参数。

配置一个 minio 通用服务

首先我们要创建一个通用服务,让他可以在我们需要用到的地方都可以使用到,而不是写死,并且这个文件模块也可以直接迁移到其他项目里去:

20240428210824

在开始之前,我们需要安装一下 minio:

pnpm add minio

安装完成之后,我们编写如下代码:

// src/minio/minio.module.ts
import { Global, Module } from "@nestjs/common";
import * as Minio from "minio";
import { ConfigService } from "@nestjs/config";

import { MinioService } from "./minio.service";

import loadMiNiOConfig from "@/config/minio.config";

@Global()
@Module({
  providers: [
    {
      provide: "MINIO_CLIENT",
      useFactory: async (configService: ConfigService) => {
        const minioConfig = loadMiNiOConfig(configService);

        const minioClient = new Minio.Client(minioConfig);

        return minioClient;
      },
      inject: [ConfigService],
    },
    MinioService,
  ],
  exports: [MinioService],
})
export class MinioModule {}

模块编写完成之后,我们要编写我们的服务了:

import { Inject, Injectable } from "@nestjs/common";
import * as Minio from "minio";
import { MulterFile } from "@webundsoehne/nest-fastify-file-upload";

@Injectable()
export class MinioService {
  constructor(
    @Inject("MINIO_CLIENT") private readonly minioClient: Minio.Client
  ) {}

  async getBuckets() {
    return await this.minioClient.listBuckets();
  }

  async uploadFile(bucketName: string, fileName: string, file: MulterFile) {
    await this.minioClient.putObject(bucketName, fileName, file.buffer);

    const expiry = 24 * 60 * 60;

    const presignedUrl = await this.minioClient.presignedUrl(
      "GET",
      bucketName,
      fileName,
      expiry
    );

    return {
      url: presignedUrl,
    };
  }

  async presignedPutUrl(
    bucketName: string,
    fileName: string,
    expiry: number = 24 * 60 * 60
  ) {
    return await this.minioClient.presignedPutObject(
      bucketName,
      fileName,
      expiry
    );
  }

  async presignedGetUrl(
    bucketName: string,
    fileName: string,
    expiry: number = 24 * 60 * 60
  ) {
    return await this.minioClient.presignedGetObject(
      bucketName,
      fileName,
      expiry
    );
  }

  async presignedPostPolicy(
    bucketName: string,
    fileName: string,
    expiry: number = 24 * 60 * 60
  ) {
    const policy = new Minio.PostPolicy();

    policy.setBucket(bucketName);
    policy.setKey(fileName);
    policy.setExpires(new Date(new Date().getTime() + expiry * 1000));

    return await this.minioClient.presignedPostPolicy(policy);
  }
}

在上面的这些代码中我们在构造函数 中通过 @Inject('MINIO_CLIENT') 装饰器注入了一个 MinIO 的客户端实例。这允许服务中的各方法使用这个客户端与 MinIO 服务进行交互。

并且编写了一些方法来对 minio 进行了一些基本的封装:

  1. getBuckets():使用 MinIO 客户端的 listBuckets() 方法来获取所有的存储桶(buckets)。这是一个异步操作,返回一个包含所有存储桶信息的列表。

  2. uploadFile(bucketName, fileName, file):将文件上传到指定的存储桶和文件名。这里 file.buffer 包含了文件的数据。

  3. presignedPutUrl(bucketName, fileName, expiry):生成一个预签名的 PUT URL,允许用户在指定的时间(默认 24 小时)内上传文件到指定的存储桶和文件名位置。

  4. presignedGetUrl(bucketName, fileName, expiry):生成一个预签名的 GET URL,允许用户在指定的时间(默认 24 小时)内从指定的存储桶下载文件。

  5. presignedPostPolicy(bucketName, fileName, expiry):创建一个预签名的 POST 策略。这个策略定义了通过 HTTP POST 方法上传文件的规则。

接下来我们将用 nestjs 编写一个简单的接口来测试一下我们所封装的服务。

20240428213613

在这里我们编写了一个简单的服务,用来获取文件上传的签名,并且为了方便,我们固定了上传之后的文件名为 077.webp:

20240428213657

访问该接口返回如下数据:

20240428213808

之后我们使用这个返回的 url 来发起一个 put 请求:

20240428213919

返回这样的数据则表示我们的文件成功上传到 minio 服务了:

20240428214032

上传成功了,预览也成功预览了:

20240428214006转存失败,建议直接上传图片文件

点击 share 我们就可以把链接分享给其他小伙伴了,但是现在这是一个本地的服务,暂时还不能分享,得部署上线才可以。

20240428214134

总结

通过上面的内容中我们通过 nestjs 结合 minio 实现了一个图床的服务,这样我们就可以抛弃阿里云的 oss 服务了。