MinIO-对象存储简单使用
MinIO
1. 基础概念
- Object:存储到minio的基本对象,如文件,字节流,Anything...
- Bucket:用来存储Object的逻辑空间。每个Bucket之间的数据是相互隔离的。对于客户端而言,就相当于一个存放文件的顶层文件夹。
- Driver:即存储数据的磁盘,在minio启动时,以参数的方式传入。MinIO中所有的对象都会存储在Drive里。
- Set:即一组Drive的集合,分布式部署根据集群规模自动划分一个或者set,每个set中的drive分布在不同的位置。一个对象存储在一个Set上。
- 一个对象存储在一个Set上
- 一个集群划分为多个set
- 一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算得出
- 一个Set中的Drive尽可能分布在不同的节点上
2. 纠删码 EC(Erasure Code)
MinIO使用纠删码机制来保证高可靠性,使用highwayhash 来处理数据损坏 (2 Protection)。纠删码,可以通过数学计算,把丢失的数据进行还原,它可以将n份原始数据,增加m份数据,并能通过n+m份中的任意n份数据,还原为原始数据。即如果有任意小于等于m份的数据失效,仍能通过剩下的数据还原出来。
3. 单机部署
中文文档的docker部署有问题,没有暴露控制台的端口。
单机部署的命令
基于centos7的部署
# 下载
wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio-20230616024106.0.0.x86_64.rpm -O minio.rpm
sudo dnf install minio.rpm
# 指定 控制台的root用户名和密码
export MINIO_ROOT_USER=admin123
export MINIO_ROOT_PASSWORD=admin123
# 指定 控制台暴露的端口,默认使用动态的端口,并指定数据存放的目录
./minio server --console-address ":50000" /data/minio-data
# 如果上述命令运行成功,可以通过控制台端口50000访问minio后台,证明运行时没问题的,就停掉。然后用下面的命令后台运行minio
nohup ./minio server --console-address ":50000" /data/minio-data &
控制台上传一个文件可以在服务器对应的目录中找到文件,服务正常运行。
基于docker的部署
docker 部署命令
docker pull docker://minio/minio
### 新版
docker run --name minio \
-p 9000:9000 \
-p 9999:9999 \
-d --restart=always \
-e "MINIO_ROOT_USER=admin123" \
-e "MINIO_ROOT_PASSWORD=admin123" \
-v /home/smy/minio-data/data:/data \
-v /home/smy/minio-data/config:/root/.minio \
minio/minio server /data \
--console-address '0.0.0.0:9999'
4. SpringBoot整合minio
遇到的问题
解决方法:
修改minio的依赖的okhttp的依赖版本
<!--minio-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
<exclusions>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
<exclusion>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.9</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.3.70</version>
</dependency>
https://juejin.cn/post/7209110611858309179
5. JAVA API 操作 MinIO
主要包括桶操作和文件操作:
- 客户端的创建
- 桶 增删改查
- 文件 上传 下载 删除(过期删除)
minio上传的大小限制,分片上传最小5MB,最大5GB;整个上传最大5TB。并且当不提供objectSize的时候,必须提供partSize(适合上传文件大小未知的场景)。
putObject 上传文件后的返回对象
6. 工具类
配置类
package org.example.config;
/**
* @author myS
* @description: MinioConfig
* @date 2023/6/21 17:21
*/
public class MinioConfig {
// 端点 单节点
public static String END_POINT = "http://192.168.1.244:9000";
// accessKey
public static String ACCESS_KEY = "admin123";
// secretKey
public static String SECRET_KEY = "admin123";
// bucketName 默认操作的bucket
public static String BUCKET_NAME = "default";
}
工具类
结合实际业务做拓展
package org.example.utils;
import io.minio.*;
import io.minio.messages.*;
import org.example.config.MinioConfig;
import javax.annotation.Nonnull;
import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* @author myS
* @description: MinioUtils
* @date 2023/6/21 17:20
*/
public class MinioUtils {
public static MinioClient minioClient = null;
static {
minioClient = MinioClient.builder()
.endpoint(MinioConfig.END_POINT)
.credentials(MinioConfig.ACCESS_KEY, MinioConfig.SECRET_KEY)
.build();
}
// 1 桶操作
/**
* 1 根据 bucketName 判断 bucket 是否存在
* @param bucketName
* @return
*/
public static boolean bucketExists(@Nonnull String bucketName) {
try {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 2 创建bucket, 使用时不需要提前判断 bucket 是否存在
* @param bucketName
* @return
*/
public static boolean makeBucket(@Nonnull String bucketName) {
try {
// 当bucket不存在的时候,创建这个bucket
if (bucketExists(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 3 根据bucketName删除一个bucket
* @param bucketName
* @return
*/
public static boolean removeBucket(@Nonnull String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 4 获取所有的 bucket, 使用时需要判断返回值是否为null,再进行后续操作
* @return
*/
public static List<Bucket> getAllBuckets() {
try {
List<Bucket> buckets = minioClient.listBuckets();
return buckets;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 5 设置 bucket 的声明周期,设置对象过期时间
* @param bucketName 指定bucket,默认是对指定的这一个桶生效
* @param expiredDays 过期时间 单位:天
* @return
*/
public static boolean setBucketLifecycle (@Nonnull String bucketName, @Nonnull Integer expiredDays) {
// 创建一个带过期时间规则的配置config
List<LifecycleRule> rules = new LinkedList<>();
rules.add(
new LifecycleRule(
Status.ENABLED,
null,
new Expiration((ZonedDateTime) null, expiredDays, null),
new RuleFilter("search"), // bucket下一级的目录
"expiredRule_1",
null,
null,
null));
LifecycleConfiguration config = new LifecycleConfiguration(rules);
// 根据 config的配置,创建一个名称为 my-bucketname 的 bucket
try {
minioClient.setBucketLifecycle(
SetBucketLifecycleArgs.builder().bucket(bucketName).config(config).build());
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
// 二 文件操作
/**
* 上传文件并返回是否上传成功
* @param bucketName
* @param inputStream 待上传文件的输入流
* @return
*/
public static boolean upload (@Nonnull InputStream inputStream, @Nonnull String bucketName, @Nonnull String objectName) {
try {
ObjectWriteResponse objectWriteResponse = minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.stream(inputStream, -1, 1024 * 1024 * 10L) // 分片上传 10M
.object(objectName)
.build());
System.out.println(objectWriteResponse.region());
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据bucket和objectName返回一个minio中的对象的输入流
* 因为可能为null,所以使用前需要先判null,避免发生其他错误
* @param bucketName
* @param objectName
* @return
*/
public static InputStream download (@Nonnull String bucketName, @Nonnull String objectName) {
try {
return minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 删除指定bucket下的名为objectName的对象
* @param bucketName
* @param objectName
* @return
*/
public static boolean removeObject(@Nonnull String bucketName, @Nonnull String objectName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 获取 当前bucket下的所有对象(只会列出一层)
* @param bucketName
* @return
*/
public static List<String> getAllObjects (@Nonnull String bucketName) {
List<String> allObjects = new ArrayList<>();
try{
Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
.bucket(bucketName)
.build());
results.forEach(item -> {
Item item1 = null;
try {
item1 = item.get();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
allObjects.add(item1.objectName());
});
} catch (Exception e) {
e.printStackTrace();
}
return allObjects;
}
}
工具类测试类
package org.example;
import io.minio.MinioClient;
import io.minio.messages.Bucket;
import org.example.utils.MinioUtils;
import org.junit.Assert;
import org.junit.Test;
import java.io.*;
import java.nio.file.Files;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
/**
* @author myS
* @description: TODO
* @date 2023/6/21 21:47
*/
public class MinioTest {
// public static MinioClient minioClient = MinioUtils.minioClient;
/**
* 测试 getAllBuckets 只能列出最外层的bucket,里层嵌套的bucket不能列出
* logs
* my-bucketname
* test
*/
@Test
public void testList () {
List<Bucket> test = MinioUtils.getAllBuckets();
test.forEach(item -> {
System.out.println(item.name());
});
}
/**
* 因为 inputStream 无法获取到 源文件的名字,所以需要指定一个 objectName,
* 这个参数如果以 / 开头,就会新建一层目录;
* 如果不以 / 开头,就会直接在 bucketName 下上传文件
* 如果文件名和目录名 重复,后面的会覆盖前面的,前面的会在删除后面的之后显示出来 ****
* @throws FileNotFoundException
*/
@Test
public void testUpload () throws FileNotFoundException {
File file = new File("C:\\Users\\10170\\Desktop\\desktop\\cnblog\\待整理.md");
InputStream inputStream = new FileInputStream(file);
// 上传的文件路径: test/hdhd
boolean upload = MinioUtils.upload(inputStream,"test","/hdhd");
System.out.println(upload);
// 上传的文件路径: test/hdhd/待整理.md
boolean upload1 = MinioUtils.upload(inputStream,"test","/hdhd/"+file.getName());
System.out.println(upload1);
}
/**
* 测试 minio 的下载函数
* 函数返回一个输入流,表示从文件写入内存中,用完close释放
* 服务器程序拿到这个输入流之后,可以通过网络返回给前端
* @throws IOException
*/
@Test
public void testDownload () throws IOException {
InputStream stream = MinioUtils.download("logs", "b50aa3fcb9fa4f64a4e295b7a996aa9a/events.json");
// stream 判 null
if (Objects.isNull(stream)) {
System.out.println("文件流为null!!!!! 下载失败");
return ;
}
// 读取流 并下载
File targetFile = new File("events.json");
FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
byte[] bytes = new byte[1024];
int length;
while ((length = stream.read(bytes)) > 0) {
fileOutputStream.write(bytes, 0, length);
}
fileOutputStream.close();
stream.close();
}
/**
* 删除 minio 文件对象测试
*/
@Test
public void testRemove() {
boolean result = MinioUtils.removeObject("test", "hdhd/待整理.md");
Assert.assertTrue(result);
}
@Test
public void getAllObjects(){
List<String> test = MinioUtils.getAllObjects("logs");
test.forEach(System.out::println);
}
}
本文来自博客园,作者:永恒&,转载请注明原文链接:https://www.cnblogs.com/Sun-yuan/p/17519379.html