找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

157

积分

0

好友

21

主题
发表于 昨天 02:20 | 查看: 4| 回复: 0

前言

在微服务架构中,OSS云存储服务通常需要集成多个存储厂商(如阿里云、腾讯云、Minio等),且未来可能新增其他厂商。如果直接修改存储厂商,会导致Controller层和Service层代码变更,违背了低耦合原则。此时,采用适配器模式进行项目开发是最佳选择!

适配器模式改造

MinioUtilsAliyunUtils作为被适配者类,各自实现原子性操作逻辑。为了统一多个OSS的接口返回,需要使用适配器模式。

被适配者类

@Component
public class MinioUtil {
    @Resource
    private MinioClient minioClient;

    /**
     * 创建Bucket桶
     */
    public void createBucket(String bucket) throws Exception {
        boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
        if(!exists) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        }
    }

    /**
     * 上传文件
     */
    public void uploadFile(InputStream inputStream, String bucket, String objectName) throws Exception {
        minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(objectName)
                .stream(inputStream, -1, 5242889L).build());
    }
}

定义目标接口,统一不同OSS的操作方法:

public interface StorageAdapter {
    void createBucket(String bucket);
    void uploadFile(MultipartFile multipartFile, String bucket, String objectName);
    String getUrl(String bucket, String objectName);
}

Minio适配器类

通过实现目标接口,转换被适配者类的接口:

@Log4j2
public class MinioStorageAdapter implements StorageAdapter {
    @Resource
    private MinioUtil minioUtil;
    @Value("${minio.url}")
    private String url;

    @Override
    @SneakyThrows
    public void createBucket(String bucket) {
        minioUtil.createBucket(bucket);
    }

    @Override
    @SneakyThrows
    public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
        minioUtil.createBucket(bucket);
        if(objectName != null) {
            minioUtil.uploadFile(multipartFile.getInputStream(), bucket, objectName + "/" + multipartFile.getOriginalFilename());
        } else {
            minioUtil.uploadFile(multipartFile.getInputStream(), bucket, multipartFile.getOriginalFilename());
        }
    }

    @Override
    public String getUrl(String bucket, String objectName) {
        return url + "/" + bucket + "/" + objectName;
    }
}

阿里云适配器类

public class AliStorageAdapter implements StorageAdapter {
    @Override
    public void createBucket(String bucket) {
        System.out.println("aliyun");
    }

    @Override
    public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
    }

    @Override
    public String getUrl(String bucket, String objectName) {
        return "aliyun";
    }
}

定义StorageConfig类

通过Nacos动态配置读取当前storageType值。新增OSS厂商时,只需添加对应的适配器类并在@Bean方法中扩展条件判断。

@Configuration
@RefreshScope
public class StorageConfig {
    @Value("${storage.service.type}")
    private String storageType;

    @Bean
    @RefreshScope
    public StorageAdapter storageAdapter() {
        if("minio".equals(storageType)) {
            return new MinioStorageAdapter();
        } else if("aliyun".equals(storageType)) {
            return new AliStorageAdapter();
        } else {
            throw new IllegalArgumentException("未找到对应的文件存储处理器");
        }
    }
}

新增FileService防腐层

提升可维护性

@Component
public class FileService {
    private final StorageAdapter storageAdapter;

    public FileService(StorageAdapter storageAdapter) {
        this.storageAdapter = storageAdapter;
    }

    public void createBucket(String bucket) {
        storageAdapter.createBucket(bucket);
    }

    public String uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
        storageAdapter.uploadFile(multipartFile, bucket, objectName);
        objectName = (StringUtils.isEmpty(objectName) ? "" : objectName + "/") + multipartFile.getOriginalFilename();
        return storageAdapter.getUrl(bucket, objectName);
    }
}

Controller层

通过注入FileService进行操作:

@RestController
@Log4j2
public class FileController {
    @Resource
    private FileService fileService;

    @PostMapping("/upload")
    public Result<String> upload(MultipartFile uploadFile, String bucket, String objectName) throws Exception {
        try {
            Preconditions.checkArgument(!ObjectUtils.isEmpty(uploadFile), "文件不能为空");
            Preconditions.checkArgument(!StringUtils.isEmpty(bucket), "bucket桶名称不能为空");
            if(log.isInfoEnabled()) {
                log.info("FileController.upload.uploadFile:{}, bucket:{}, objectName:{}", uploadFile.getOriginalFilename(), bucket, objectName);
            }
            String url = fileService.uploadFile(uploadFile, bucket, objectName);
            return Result.ok(url);
        } catch (Exception e) {
            log.info("FileController.upload.error:{}", e.getMessage(), e);
            return Result.fail("上传文件失败");
        }
    }
}

Nacos搭建

Nacos部署

服务器需开启8848、9848端口:

docker search nacos
docker pull nacos/nacos-server
# 启动容器
docker run -d \
  --name nacos \
  --privileged \
  --cgroupns host \
  --env JVM_XMX=256m \
  --env MODE=standalone \
  --env JVM_XMS=256m \
  -p 8848:8848/tcp \
  -p 9848:9848/tcp \
  --restart=always \
  -w /home/nacos \
  nacos/nacos-server

参数说明:

  • --privileged:赋予容器扩展特权
  • --cgroupns host:使用宿主机cgroup命名空间
  • --env MODE=standalone:单机模式运行
  • 8848:Nacos服务端端口
  • 9848:客户端gRPC请求端口

引入Nacos客户端依赖

需引入nacos-config和log4j2依赖:

<!-- Nacos配置中心 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
    <version>2.4.2</version>
</dependency>

配置文件设置

在bootstrap.yml中配置Nacos连接:

spring:
  application:
    name: jc-club-oss
  profiles:
    active: dev
  cloud:
    nacos:
      server-addr: 117.72.118.73:8848
      config:
        file-extension: yaml

配置管理

在Nacos控制台添加配置:

  • DataId: jc-club-oss-dev.yaml
  • 配置内容包含storage.service.type参数

热更新配置

在配置类添加@RefreshScope注解,实现配置动态更新:

@Configuration
@RefreshScope
public class StorageConfig {
    // 配置内容
}

测试验证

  1. 配置storage.service.type为aliyun时,返回阿里云标识
  2. 动态修改为minio后,文件成功上传至Minio
  3. Nacos日志显示配置更新记录:Refresh keys changed: [storage.service.type]

通过SpringBoot框架与Nacos的集成,实现了OSS存储的无感动态切换,提升了系统的扩展性和维护性。

您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-1 14:12 , Processed in 0.058663 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表