weblog/doc/8、对象存储服务搭建与开发/8.4 服务注册到 Nacos、动态配置、Minio 策略类逻辑补充.md
2025-02-17 10:05:44 +08:00

12 KiB
Raw Blame History

本小节中,我们继续完善对象存储服务的相关功能。

服务注册到 Nacos

添加依赖

首先是将服务注册到 Nacos 中。编辑 xiaohashu-oss-biz 模块的 pom.xml 文件,添加如下依赖:

        // 省略...
        
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</artifactId>
        </dependency>

        <!-- 服务发现 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        
        // 省略...

创建配置

依赖添加完毕后,在 /config 文件下,创建 bootstrap.yml 文件,配置如下:

spring:
  application:
    name: xiaohashu-oss # 应用名称
  profiles:
    active: dev # 默认激活 dev 本地开发环境
  cloud:
    nacos:
      discovery:
        enabled: true # 启用服务发现
        group: DEFAULT_GROUP # 所属组
        namespace: xiaohashu # 命名空间
        server-addr: 127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址

Tip

: 小伙伴们可以直接从认证服务中复制过来,将 spring.application.name 应用名称改一下就行。

配置完成后,重启服务。登录到 Nacos 控制台,查看服务列表,确认一下 xiaohashu-oss 服务是否注册成功:

Nacos 配置中心

《6.3节》 中,我们做了个测试,通过 Nacos 配置中心的能力,实现了动态加载 Bean 。刚好,上小节中正需要按需初始化文件策略类到 Spring 容器中,必须整上。继续编辑 pom.xml 文件,添加配置中心依赖,如下:

        <!-- 配置中心 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

接着,登录到 Nacos 控制台,进入到配置列表,选择 xiaohashu 命名空间,并创建一个 xiaohashu-oss-dev.yaml 对象存储配置,如下图所示:

填写相关配置项:

  • Data ID : 填写 xiaohashu-oss-dev.yaml

  • Group : 默认即可;

  • 添加配置相关描述,方便后续维护;

  • 配置格式:选择 YAML 格式;

  • 配置内容,如下:

    storage:
      type: minio # 对象存储类型
    

添加配置

接着,编辑 bootstrap.yml 文件,添加配置中心相关配置:

spring:
  // 省略...
  cloud:
    nacos:
      config:
        server-addr: http://127.0.0.1:8848 # 指定 Nacos 配置中心的服务器地址
        prefix: ${spring.application.name} # 配置 Data Id 前缀,这里使用应用名称作为前缀
        group: DEFAULT_GROUP # 所属组
        namespace: xiaohashu # 命名空间
        file-extension: yaml # 配置文件格式
        refresh-enabled: true # 是否开启动态刷新

添加 @RefreshScope 注解

然后,编辑 FileStrategyFactory 策略工厂类,分别为类和方法都添加上 @RefreshScope 注解:

package com.quanxiaoha.xiaohashu.oss.biz.factory;

import com.quanxiaoha.xiaohashu.oss.biz.strategy.FileStrategy;
import com.quanxiaoha.xiaohashu.oss.biz.strategy.impl.AliyunOSSFileStrategy;
import com.quanxiaoha.xiaohashu.oss.biz.strategy.impl.MinioFileStrategy;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author: 犬小哈
 * @date: 2024/6/27 19:44
 * @version: v1.0.0
 * @description: TODO
 **/
@Configuration
@RefreshScope
public class FileStrategyFactory {

    @Value("${storage.type}")
    private String strategyType;

    @Bean
    @RefreshScope
    public FileStrategy getFileStrategy() {
        if (StringUtils.equals(strategyType, "minio")) {
            return new MinioFileStrategy();
        } else if (StringUtils.equals(strategyType, "aliyun")) {
            return new AliyunOSSFileStrategy();
        }

        throw new IllegalArgumentException("不可用的存储类型");
    }

}

这里就不再截图测试效果了,小伙伴们可以自行重启服务,测试一波动态加载 Bean 功能是否正常。

Minio 策略类上传文件

最后,我们再来把 MinioFileStrategy 策略类的上传文件逻辑补充一下。

添加 Minio 依赖

编辑项目最外层的 pom.xml 文件,声明 minio 的版本号与依赖,代码如下:

    <properties>
      	// 省略...
      	
        <minio.version>8.2.1</minio.version>
    </properties>

    <!-- 统一依赖管理 -->
    <dependencyManagement>
        <dependencies>
           	// 省略...

            <!-- 对象存储 Minio -->
            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>${minio.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

然后,编辑 xiaohashu-oss-bizpom.xml, 引入该依赖:

        <!-- 对象存储 Minio -->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
        </dependency>

添加配置

依赖添加完毕后,编辑 application-dev.yml 本地测试环境配置,添加 minio 相关配置:

如下:

#=================================================================
# minio (上传图片需要,需配置成自己的地址)
#=================================================================
minio:
  endpoint: http://127.0.0.1:9000
  accessKey: quanxiaoha
  secretKey: quanxiaoha

注意:关于整合如何 Minio,在星球第一个项目的 《9.3 节》 已经讲的比较详细了,不清楚的小伙伴们,可翻阅一下,这里就直接把相关代码复制过来用了。

添加配置类

创建 /config 包,添加 minio 相关配置类,如下:

package com.quanxiaoha.xiaohashu.oss.biz.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author: 犬小哈
 * @url: www.quanxiaoha.com
 * @date: 2023-05-11 8:49
 * @description: TODO
 **/
@ConfigurationProperties(prefix = "minio")
@Component
@Data
public class MinioProperties {
    private String endpoint;
    private String accessKey;
    private String secretKey;
}

初始化 MinioClient 客户端配置类如下:

package com.quanxiaoha.xiaohashu.oss.biz.config;

import io.minio.MinioClient;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author: 犬小哈
 * @url: www.quanxiaoha.com
 * @date: 2023-05-11 8:49
 * @description: TODO
 **/
@Configuration
public class MinioConfig {

    @Resource
    private MinioProperties minioProperties;

    @Bean
    public MinioClient minioClient() {
        // 构建 Minio 客户端
        return MinioClient.builder()
                .endpoint(minioProperties.getEndpoint())
                .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
                .build();
    }
}

策略类逻辑完善

编辑 MinioFileStrategy 文件策略类,补充上文件上传至 minio 的逻辑代码,如下:

package com.quanxiaoha.xiaohashu.oss.biz.strategy.impl;

import com.quanxiaoha.xiaohashu.oss.biz.config.MinioProperties;
import com.quanxiaoha.xiaohashu.oss.biz.strategy.FileStrategy;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.util.UUID;

/**
 * @author: 犬小哈
 * @date: 2024/6/27 19:47
 * @version: v1.0.0
 * @description: TODO
 **/
@Slf4j
public class MinioFileStrategy implements FileStrategy  {

    @Resource
    private MinioProperties minioProperties;

    @Resource
    private MinioClient minioClient;

    @Override
    @SneakyThrows
    public String uploadFile(MultipartFile file, String bucketName) {
        log.info("## 上传文件至 Minio ...");

        // 判断文件是否为空
        if (file == null || file.getSize() == 0) {
            log.error("==> 上传文件异常:文件大小为空 ...");
            throw new RuntimeException("文件大小不能为空");
        }

        // 文件的原始名称
        String originalFileName = file.getOriginalFilename();
        // 文件的 Content-Type
        String contentType = file.getContentType();

        // 生成存储对象的名称(将 UUID 字符串中的 - 替换成空字符串)
        String key = UUID.randomUUID().toString().replace("-", "");
        // 获取文件的后缀,如 .jpg
        String suffix = originalFileName.substring(originalFileName.lastIndexOf("."));

        // 拼接上文件后缀,即为要存储的文件名
        String objectName = String.format("%s%s", key, suffix);

        log.info("==> 开始上传文件至 Minio, ObjectName: {}", objectName);

        // 上传文件至 Minio
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(contentType)
                .build());

        // 返回文件的访问链接
        String url = String.format("%s/%s/%s", minioProperties.getEndpoint(), bucketName, objectName);
        log.info("==> 上传文件至 Minio 成功,访问路径: {}", url);
        return url;
    }
}

业务层返回文件访问链接

编辑 FileServiceImpl 文件业务实现类,将上传成功后,图片的访问链接进行返回,代码如下:

package com.quanxiaoha.xiaohashu.oss.biz.service.impl;

import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.xiaohashu.oss.biz.service.FileService;
import com.quanxiaoha.xiaohashu.oss.biz.strategy.FileStrategy;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

/**
 * @author: 犬小哈
 * @date: 2024/4/11 17:12
 * @version: v1.0.0
 * @description: TODO
 **/
@Service
@Slf4j
public class FileServiceImpl implements FileService {

    @Resource
    private FileStrategy fileStrategy;

    private static final String BUCKET_NAME = "xiaohashu";

    @Override
    public Response<?> uploadFile(MultipartFile file) {
        // 上传文件
        String url = fileStrategy.uploadFile(file, BUCKET_NAME);

        return Response.success(url);
    }
}

自测一波

重启服务,测试一波文件上传接口,如下图所示,可以看到成功返回了图片的访问链接:

小作业:全局异常捕获器

最后,给大家留个小作业,为对象存储服务添加全局异常捕获器,结构如下,代码可以从认证服务中复制一份过来,稍作修改即可:

注意,记得将对象存储的错误状态码标识修改一下,如 OSS-10000, 每个服务的错误码都应具有唯一性,不能搞混了,如下图所示:

哪里不清楚的小伙伴,也可以下载本小节的源码,与自己写的代码对比对比。

本小节源码下载

https://t.zsxq.com/A3iUk