12 KiB
本小节中,我们继续完善对象存储服务的相关功能。
服务注册到 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-biz 的 pom.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, 每个服务的错误码都应具有唯一性,不能搞混了,如下图所示:
哪里不清楚的小伙伴,也可以下载本小节的源码,与自己写的代码对比对比。