目的
尝试SpringBoot与MinIO的整合,使得在SpringBoot项目中能优雅的使用分布式对象存储服务MinIO。
分析
MinIO 提供了一个 mc 客户端工具。 同时提供了 Java 、 Golang 、 Python 、 JavaScript 、 .NET 等多种语言的SDK。可以参考MinIO官方 Java SDK文档 完成 SpringBoot 与 MinIO 的整合。
操作
1、创建 SpringBoot 项目
创建过程忽略,pom.xml 中引入的依赖有:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2、引入 MinIO SDK 包
根据 MinIO 官方文档介绍,引入其 Maven 依赖
<!-- MinIO -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.1</version>
</dependency>
3、编写 MinIO 配置文件
在 application.yml 中配置 MinIO 用到的用户名、密码及URL
minio:
endpoint: http://localhost:9000/
access-key: admin
secret-key: admin123
4、创建 MinIOConfig 配置类
package com.xzbd.minio.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
private String endpoint;
private String accessKey;
private String secretKey;
}
5、将 MinioClient 交由Spring管理
这一步是本此尝试的核心部分。代码如下:
package com.xzbd.minio.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.minio.MinioClient;
@Configuration
public class MinioClientConfig {
@Autowired
private MinioConfig minioConfig;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(minioConfig.getEndpoint())
.credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
.build();
}
}
6、使用 MinioClient 示例
本节按照 Java Web MVC 开发模式给出 MinIO 使用示例 ,出两个接口
a) 获取 bucket 列表
BucketController
package com.xzbd.minio.controller;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Objects;
import com.xzbd.minio.vo.BucketVo;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.minio.MinioClient;
import io.minio.errors.ErrorResponseException;
import io.minio.errors.InsufficientDataException;
import io.minio.errors.InternalException;
import io.minio.errors.InvalidResponseException;
import io.minio.errors.ServerException;
import io.minio.errors.XmlParserException;
import io.minio.messages.Bucket;
@RestController
@RequestMapping("/bucket")
public class BucketController {
@Autowired
private MinioClient minioClient;
@GetMapping("/list")
public List<BucketVo> getAllBoukes() {
try {
List<Bucket> list = minioClient.listBuckets();
if (Objects.isNull(list))
return Lists.newArrayList();
return BucketVo.fromBucketList(list);
} catch (InvalidKeyException | ErrorResponseException | InsufficientDataException | InternalException
| InvalidResponseException | NoSuchAlgorithmException | ServerException | XmlParserException
| IOException e) {
e.printStackTrace();
return Lists.newArrayList();
}
}
}
BucketVo
package com.xzbd.minio.vo;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import io.minio.messages.Bucket;
import lombok.Builder;
import lombok.Getter;
/**
* MinIO中Bucket的VO
*/
@Builder
@Getter
public class BucketVo {
private String name;
private ZonedDateTime creteDateTime;
public static BucketVo fromBucket(Bucket bkt) {
return BucketVo.builder()
.name(bkt.name())
.creteDateTime(bkt.creationDate())
.build();
}
public static List<BucketVo> fromBucketList(List<Bucket> bks) {
if (Objects.isNull(bks)) {
return null;
}
return bks.stream().map(item -> fromBucket(item)).collect(Collectors.toList());
}
}
b) 获取 object 列表
ObjectController
package com.xzbd.minio.controller;
import java.util.List;
import java.util.Objects;
import javax.annotation.Resource;
import com.google.common.collect.Lists;
import com.xzbd.minio.vo.ObjectVo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.minio.ListObjectsArgs;
import io.minio.MinioClient;
import io.minio.Result;
import io.minio.messages.Item;
@RestController
@RequestMapping("/obj")
public class ObjectController {
@Resource
private MinioClient minioClient;
@GetMapping("/list/{bktName}")
public List<ObjectVo> listObjects(@PathVariable String bktName) {
// 构造参数
ListObjectsArgs args = ListObjectsArgs.builder().bucket(bktName).build();
// 查询数据
Iterable<Result<Item>> items = minioClient.listObjects(args);
List<ObjectVo> objVos = Lists.newArrayList();
// 检查、转换 并 返回结果
if (Objects.isNull(items)) {
return objVos;
}
return ObjectVo.fromResItem(items);
}
}
ObjectVo
package com.xzbd.minio.vo;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Objects;
import com.google.common.collect.Lists;
import io.minio.Result;
import io.minio.messages.Item;
import lombok.Builder;
import lombok.Getter;
/**
* MinIO中对象的VO
*/
@Getter
@Builder
public class ObjectVo extends Item {
private String etag; // except DeleteMarker
private String objectName;
private ZonedDateTime lastModified;
// private Owner owner;
private long size; // except DeleteMarker
private String storageClass; // except DeleteMarker, not in case of MinIO server.
private boolean isLatest; // except ListObjects V1
private String versionId; // except ListObjects V1
// private Metadata userMetadata;
@Builder.Default
private boolean isDir = false;
// private String encodingType = null;
public static ObjectVo fromItem(Item item) {
if (Objects.isNull(item)) {
return null;
}
return ObjectVo.builder()
.etag(item.etag())
.objectName(item.objectName())
.lastModified(item.lastModified())
.size(item.size())
.storageClass(item.storageClass())
.isLatest(item.isLatest())
.versionId(item.versionId())
.isDir(item.isDir())
.build();
}
public static List<ObjectVo> fromResItem(Iterable<Result<Item>> items) {
List<ObjectVo> list = Lists.newArrayList();
if (Objects.isNull(items)) {
return list;
}
items.forEach(resItem -> {
try {
list.add(fromItem(resItem.get()));
} catch (Exception e) {
e.printStackTrace();
}
});
return list;
}
}
测试
启动项目
访问 localhost:8080/bkt/list
获取bucket列表,
[{"name":"bk01","creteDateTime":"2022-05-31T11:49:41.872Z"}]
访问 http://localhost:8080/obj/list/bk01
获取名称为 bk01 的 bucket 中所有 object
[{"etag":"\"0dbb9cbadd98e8cde4bbfd05af544686\"","objectName":"devops02.png","lastModified":"2022-05-31T11:50:12.888Z","size":130159,"storageClass":"STANDARD","versionId":null,"dir":false,"latest":false,"deleteMarker":false}]
总结
文章介绍了 SpringBoot 整合 MinIO 的全过程,并附上源码。
项目源码见xzbd/spring-boot-demos
MinIO 存储服务搭建见Windows 下使用Docker 安装 minio
Minio 中文文档