SpringBoot整合Minio文件存储服务

MinIO简介

MinIO 是一款基于 Go 语言发开的高性能、分布式的对象存储系统,客户端支持 Java,Net,Python,Javacript,Golang语言。
MinIO 的主要目标是作为私有云对象存储的标准方案,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据、容器和虚拟机镜像等,而一个对象文件可以是任意大小,从几 kb 到最大 5T。

它具有分布式,高可用性和水平扩展的特点,它非常适合用于大规模数据存储和分析。其优点包括低延迟、高吞吐量、易于部署和管理

截止目前,MinIO 在 Github 上有 43.7k Star。

国内阿里巴巴、腾讯、百度、华为、中国移动、中国联通等企业都有在使用 MinIO,甚至不少商业公司二次开发 MinIO 来提供商业化的云存储产品。

使用 Docker 部署 MinIO 服务

安装Docker&Compose(略)

使用docker-compose安装并运行MinIO容器

docker-compose.yaml 文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

networks:
minio-network:
external: true
services:
minio:
container_name: minio-data
environment:
MINIO_ROOT_PASSWORD: minio_admin
MINIO_ROOT_USER: minio_kikock
image: minio/minio:latest
networks:
- minio-network
ports:
- 9001:9001
- 9000:9000
restart: always
volumes:
- ./data:/data
- ./certs:/root/.minio/certs


命令行解释:

  • networks网络

    • 创建一个网络名称为minio-network
  • services服务

    • container_name: 设置容器名称为 minio-data。
    • environment: 指定了 Minio 根用户账号 (minio_kikock) 及密码 (minio_admin)。
    • image: 使用官方的 Minio 镜像最新版本 (minio/minio:latest)。
    • networks: 将服务加入到 minio-network 网络。
    • ports: 映射主机端口 9001 到容器端口 9001 (用于 Minio 浏览器) 和 9000 到 9000 (用于 Minio 服务 API)。
    • restart: 设置为 always 以确保容器异常退出后自动重启。
    • volumes: 挂载主机目录 ./data 和 ./certs 到容器相应目录,保持数据和证书的持久化。

将yaml文件上传到服务器执行docker-compose -up -d 创建并运行容器

ps: 也可以使用1Panel服务器管理控制面板安装更简单

最后在浏览器中访问http://服务器IP:9001,即可访问到MinIO的控制台。**

可以输入账号 admin,密码 password 进行登录,进入首页。

776e6ada088ae0ab841d1.png

创建一个 Bucket 存储桶,用于稍后文件的上传操作。

a4f48b3969689530b47ee.png
6ea8123626b7684574e72.png

创建并保存好生成Access Key 和 Secret Key**.

a1df89d71e8e9c317f31d.png
ab637a34ae4a0c36fd377.png

至此,我们已经基本完成了MinIO的简单配置,当然如果想要更安全,同样是可以像使用第三方对象存储服务一样创建用户组,分配权限等诸如此类的操作。

测试一下

上传一张图片并访问来测试一下是否搭建成功。

935765ffc59e9c6b96910.png
d83ff2cafb37da2143ab3.png

可以看到成功上传了图片点击这里可以预览

11a84f426840824a1315f.png

接下来我们在浏览器访问 http://服务器IP:9000/{bucket存储桶名字}/{name图片后缀名} 来进行访问。
sdn.png,所以最终的访问路径是 http://服务器IP:9000/guanzhi/csdn.png

Spring boot集成Minio并应用

一、依赖的引入

1.1、maven项目

1
2
3
4
5
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.11</version>
</dependency>

二、minio配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

@Data
@Configuration
public class MinioConfig{
//
@Value("${file.minio.endpoint}")
private String endpoint;
@Value("${file.minio.accessKey}")
private String accessKey;
@Value("${file.minio.secretKey}")
private String secretKey;
@Value("${file.minio.bucket}")
private String bucketName;

@Bean
public MinioClient minioClient(){
MinioClient minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
return minioClient;
}

}

其中四个变量为application.yml中的配置,MinioConfig.java配置文件创建了minio分布式存储系统的操作客户端minioClient并交给springboot进行管理,且创建了名为java-bucket的桶。

1
2
3
4
5
6
7
8
9
10
# 分布式存储minio相关配置
file:
minio:
# 分布式存储的服务地址,要做集群的话则是通过nginx配置负载均衡,该地址指向nginx入口
endpoint: http://127.0.0.1:9001
# 分布式存储系统对应的用户名和密码
accessKey: minioadmin
secretKey: minioadmin
# 所有文件均存储在该名称对应的桶中
bucket: java-bucket

三、通过minioClient操作文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

@Service
@Slf4j
public class MinioUtils{
@Autowired
MinioConfig minioConfig;
@Autowired
MinioClient minioClient;

//获取列表
public List<String> listObjects(){
List<String> list = new ArrayList<>();
try {

ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder()
.bucket(minioConfig.getBucketName())
.build();

Iterable<Result<Item>> results = minioClient.listObjects(listObjectsArgs);
for (Result<Item> result : results) {
Item item = result.get();
log.info(item.lastModified() + ", " + item.size() + ", " + item.objectName());
list.add(item.objectName());
}
} catch (Exception e) {
log.error("错误:" + e.getMessage());
}
return list;
}

//删除
public void deleteObject(String objectName){
try {
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(objectName)
.build();
minioClient.removeObject(removeObjectArgs);
} catch (Exception e) {
log.error("错误:" + e.getMessage());
}
}

//上传
public void uploadObject(InputStream is, String fileName, String contentType){
try {
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(fileName)
.contentType(contentType)
.stream(is, is.available(), -1)
.build();
minioClient.putObject(putObjectArgs);
is.close();
} catch (Exception e) {
log.error("错误:" + e.getMessage());
}
}

//获取minio中地址
public String getObjectUrl(String objectName){
try {
GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(minioConfig.getBucketName())
.object(objectName)
.expiry(7, TimeUnit.DAYS)
.build();
return minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
} catch (Exception e) {
e.printStackTrace();
log.error("错误:" + e.getMessage());
}
return "";
}


//下载minio服务的文件
public InputStream getObject(String objectName){
try {
GetObjectArgs getObjectArgs = GetObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(objectName)
.build();
return minioClient.getObject(getObjectArgs);
} catch (Exception e) {
log.error("错误:" + e.getMessage());
}
return null;
}


}


四、通过MinioFileController进行调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

@RestController
@RequestMapping("/minio")
@Tag(name = "MinIO储存库接口")
public class MinioFileController{

@Autowired
MinioUtils minioService;

@Autowired
MinioConfig minioConfig;
//列表
@GetMapping("/list")
@Operation(summary = "获取MinIO储存库附件列表")
public AjaxResult list(){
List<String> strings = minioService.listObjects();
return AjaxResult.success(strings);
}

//删除
@PutMapping("/delete")
@Operation(summary = "删除MinIO储存库附件")
@Parameters({
@Parameter(name = "filename", description = "文件名称", required = true, in = ParameterIn.QUERY)
})
public AjaxResult delete(@RequestParam String filename){
minioService.deleteObject(filename);
return AjaxResult.success(true);
}

//上传文件
@PostMapping("/upload")
@Operation(summary = "上传附件到MinIO储存库")
@Parameters({
@Parameter(name = "file", description = "文件", required = true, in = ParameterIn.QUERY)
})
public AjaxResult upload(@RequestParam("file") MultipartFile file){
try {
//http://119.6.253.214:8002/jx-minio/1720662975260.jpg
StringBuilder sb =new StringBuilder();
sb.append(minioConfig.getEndpoint()).append("/").append(minioConfig.getBucketName()).append("/");
InputStream is = file.getInputStream(); //得到文件流
String originalFilename = file.getOriginalFilename(); //文件名
String newFileName = DateUtils.dateTime()+System.currentTimeMillis() + "." + StringUtils.substringAfterLast(originalFilename, ".");
String contentType = file.getContentType(); //文件类型
sb.append(newFileName);
minioService.uploadObject(is, newFileName, contentType);
AjaxResult ajax = AjaxResult.success();
ajax.put("url", sb.toString());
ajax.put("fileName", sb.toString());
ajax.put("newFileName", newFileName); //minio服务器文件名称
ajax.put("originalFilename", originalFilename); //上传文件名称
return ajax;
} catch (Exception e) {
throw new ServiceException("上传失败",HttpStatus.ERROR);
}
}

//下载minio服务的文件
@GetMapping("/download/{filename}")
@Operation(summary = "下载MinIO储存库附件")
@Parameters({
@Parameter(name = "filename", description = "文件名称", required = true, in = ParameterIn.QUERY)
})
public void download(@PathVariable("filename") String filename, HttpServletResponse response){
try {
InputStream fileInputStream = minioService.getObject(filename);
// todo 完善文件命名逻辑
String newFileName = System.currentTimeMillis() + "." + StringUtils.substringAfterLast(filename, ".");
response.setHeader("Content-Disposition", "attachment;filename=" + newFileName);
response.setContentType("application/force-download");
response.setCharacterEncoding("UTF-8");
IOUtils.copy(fileInputStream, response.getOutputStream());
} catch (Exception e) {
throw new ServiceException("下载失败",HttpStatus.ERROR);
}
}

//获取minio文件的下载地址
@GetMapping("/getHttpUrl")
@Operation(summary = "获取MinIO储存库附件地址")
public AjaxResult getHttpUrl(@RequestParam String filename){
try {
String url = minioService.getObjectUrl(filename);
return AjaxResult.success(url);
} catch (Exception e) {
throw new ServiceException( e.getMessage(),HttpStatus.ERROR);
}
}


}


五、最后调用接口测试一下

27099c415586596ee62e9.png