类似于FastDFS/HDFS的一个文件存储服务!
SpringBoot整合MinIO实现分布式文件服务!
#MinIO简介?
Minio 是个基于 Golang 编写的开源对象存储套件,基于Apache License v2.0开源协议,虽然轻量,却拥有着不错的性能。它兼容亚马逊S3云存储服务接口。可以很简单的和其他应用结合使用,例如 NodeJS、Redis、MySQL等。
#1. 应用场景
MinIO 的应用场景除了可以作为私有云的对象存储服务来使用,也可以作为云对象存储的网关层,无缝对接 Amazon S3 或者 MicroSoft Azure
#2. 特点
-
高性能:作为一款高性能存储,在标准硬件条件下,其读写速率分别可以达到 55Gb/s 和 35Gb/s。并且MinIO 支持一个对象文件可以是任意大小,从几kb到最大5T不等。
-
可扩展:不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并且支持跨越多个数据中心。
-
云原生:容器化、基于K8S的编排、多租户支持。
-
Amazon S3兼容:使用 Amazon S3 v2 / v4 API。可以使用Minio SDK,Minio Client,AWS SDK 和 AWS CLI 访问Minio服务器。
-
SDK支持:
-
GO SDK:GitHub - minio/minio-go: MinIO Go client SDK for S3 compatible object storage(opens new window)
-
JavaSDK:GitHub - minio/minio-java: MinIO Client SDK for Java(opens new window)
-
PythonSDK:GitHub - minio/minio-py: MinIO Client SDK for Python(opens new window)
-
-
图形化界面:有操作页面
-
支持纠删码:MinIO使用纠删码、Checksum来防止硬件错误和静默数据污染。在最高冗余度配置下,即使丢失1/2的磁盘也能恢复数据。
功能很强大~
源码地址:GitHub - minio/minio: High Performance Object Storage for AI(opens new window)
中文文档地址:MinIO | 高性能, Kubernetes原生对象存储(opens new window)
#安装MinIO
安装非常简单,这里使用docker安装,步骤如下:
#1. 获取镜像
执行命令如下:
docker pull minio/minio
#2. 启动镜像
执行命令如下:
mkdir -p /opt/mnt/data /opt/mnt/conf
docker run -p 9000:9000 -p 9099:9099 --name minio -d --restart=always -e "MINIO_ACCESS_KEY=admin" -e "MINIO_SECRET_KEY=admin123456789" -v /opt/mnt/data:/data -v /opt/mnt/config:/root/.minio minio/minio server --console-address ":9000" --address ":9099" /data
命令解释如下:
-
-p:9000是图形界面的端口,9001是API的端口,在使用SDK连接需要用到
-
MINIO_ACCESS_KEY:指定图形界面的用户名
-
MINIO_SECRET_KEY:指定图形界面的密码
按照上述两个步骤启动成功即可。
注意:ACCESS_KEY 长度最少3个字符,SECRET_KEY 长度最少8个字符
#3. 图形界面操作
安装成功后直接访问地址:http:/ip:9000/login,如下:
输入用户名和密码登录成功后,如下:
菜单很多,这里就不再详细介绍了,笔者这里直接在Buckets菜单中创建一个桶为test,如下图:
并且设置这个桶的隐私规则为public,如下:
MinIO到此已经安装设置成功了
#SpringBoot整合MinIO上传文件
要实现SDK上传需要检查存放图片data文件是否为最高权限,不然上传接口会报错:
S3 API Requests must be made to API port
原因:权限不够不能创建对应文件路径
解决方案:
检查docker映射的data目录 /opt/mnt/data权限,如果权限不够 chmod 777 /opt/mnt/data
虽然MinIO在图形界面提供了手动上传的操作,但是也可以通过SDK的方式去上传,下面介绍一下Spring Boot 整合MinIO上传文件。
#1. 获取accessKey和secretKey
这里的accessKey和secretKey并不是图形界面登录名和密码,获取很简单,直接在图形界面中操作,如下图:
#2. 添加依赖
添加MinIO的依赖,如下:
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.3</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.10.0</version>
</dependency>
#3. 添加配置
minio:
# minio地址
endpoint: http://localhost:9000
# 账户
username: minioadmin
# 密码
password: minioadmin
defaultBucketName: test
#4. 创建配置类,用于生成MinioClient
/**
* @Description minio配置类
*/
@Configuration
public class MinioConfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.username}")
private String username;
@Value("${minio.password}")
private String password;
@Value("${minio.defaultBucketName}")
private String defaultBucketName;
@Bean
public MinioClient minioClient(){
return MinioClient.builder().credentials(username, password).endpoint(endpoint).build();
}
}
#5.创建一个返回实体类,方便规范返回信息
@Data
public class MinioReturn {
/**
* 文件地址
*/
private String path;
/**
* 原始文件名
*/
private String inputName;
/**
* 最终文件名
*/
private String outPutName;
}
#6.创建MinioTemplate
类,用来书写minio工具类
@Component
public class MinioTemplate {
@Autowired
private MinioClient minioClient;
private static final String SLASH = "/";
@Value("${minio.defaultBucketName}")
private String defaultBucketName;
@Value("${minio.endpoint}")
private String endpoint;
/**
* 创建桶
*
* @param bucketName
* @throws Exception
*/
public void makeBucket(String bucketName) throws Exception {
BucketExistsArgs args = BucketExistsArgs.builder().bucket(bucketName).build();
if (!minioClient.bucketExists(args)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}
/**
* 上传文件
*
* @param file
* @return
* @throws Exception
*/
public MinioReturn putFile(MultipartFile file) throws Exception {
return putFile(file, file.getOriginalFilename(), defaultBucketName);
}
public MinioReturn putFile(MultipartFile file, String fileName, String bucketName) throws Exception {
if (bucketName == null || bucketName.length() == 0) {
bucketName = defaultBucketName;
}
makeBucket(bucketName);
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build());
return new MinioReturn(fileLink(bucketName, fileName), file.getOriginalFilename(), fileName);
}
/**
* 删除文件
*
* @param bucketName
* @param fileName
* @throws Exception
*/
public void removeFile(String bucketName, String fileName) throws Exception {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucketName == null || bucketName.length() == 0 ? defaultBucketName : bucketName)
.object(fileName)
.build());
}
@SneakyThrows
private String fileLink(String bucketName, String fileName) {
return endpoint.concat(SLASH).concat(bucketName).concat(SLASH).concat(fileName);
}
private String getFileName(String fileName) {
return getFileName(null, fileName);
}
private String getFileName(String prefix, String fileName) {
String fileNamePre = fileName;
String fileType = "";
int index = fileName.lastIndexOf(".");
if (index > 0) {
fileNamePre = fileName.substring(0, index);
fileType = fileName.substring(index);
}
String name = UUID.randomUUID().toString().replace("-", "");
if (!org.springframework.util.StringUtils.isEmpty(fileNamePre)) {
name = fileNamePre + "-" + name + fileType;
}
if (!StringUtils.isEmpty(prefix)) {
name = prefix + "-" + name;
}
return name;
}
}
#7.书写控制类,用于测试
@RestController
@RequestMapping("minio")
@AllArgsConstructor
public class MinioController {
private final MinioTemplate minioTemplate;
@PostMapping("/upload")
@ResponseBody
public MinioReturn upload(MultipartFile file) throws Exception {
return minioTemplate.putFile(file);
}
@PostMapping("/remove")
@ResponseBody
public String remove(String fileName, String bucketName) throws Exception{
minioTemplate.removeFile(bucketName, fileName);
return "success";
}
}
#8. 测试
我们查看minio中,自动创建了桶,并且文件也上传成功了
同样再测试一下删除接口
查看minio中删除成功
如果想通过配置域名映射minio服务器访问指定图片:
桶权限设置为public
上述代码创建的桶默认是private
的,如果想要通过客户端代码调整桶权限的话,可以通过minioClient.setBucketPolicy
方法,设置完后可以通过返回的地址进行访问,如下所示(如果想要通过外网访问,给对应的内网地址端口做个外网映射即可)
开启权限public会有个安全问题,那就是当你访问桶路径时,会发现会把所有桶下的文件列出来,这样只要再拼接上文件名就能访问所有文件了
要解决这个问题,只需要将权限设置为custom
,然后将"s3:ListBucket"
取消即可
再次访问会发现权限禁止,而加上文件名后是可以正常查看的
当然,也可以使用AmazonS3 云存储服务接口
相关文档:Amazon S3对象执行操作 - AWS SDK for Java1.x