Minio
什么是Minio?
Minio 是一个基于 Apache License v2.0 开源协议的高性能对象存储服务器。它可以用于存储各种类型的文件,包括图片、视频、文档等,并且能够以分布式的方式部署,提供高可用性和扩展性。
- Minio 具有简洁的 API,对于开发者来说很容易理解和使用。它支持多种编程语言,如 Java、Python、JavaScript 等。以 Python 为例,使用 Minio Python SDK 可以很方便地实现文件的上传、下载和管理操作。
- Minio 采用了分布式架构和优化的数据存储算法,能够提供很高的读写性能。它可以处理大量的并发请求,适合在对存储性能要求较高的场景下使用,如大数据存储、云计算环境中的存储服务等。
- Minio 支持数据的加密存储,通过加密密钥可以确保存储数据的安全性。同时,它提供了数据冗余和备份机制,例如可以将数据复制到多个节点,防止数据丢失。在分布式部署中,即使部分节点出现故障,依然可以通过其他节点的数据副本恢复数据。
- 可以方便地通过添加存储节点来扩展存储容量和性能。这种分布式的扩展方式使得 Minio 能够适应不断增长的存储需求,无论是存储的数据量增大还是访问请求增多,都可以通过合理地添加节点来满足要求。
Minio的 Maven 依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.13</version>
</dependency>
配置文件配置 Minio
minio:
endpoint: http://10.100.22.125:9000 # MinIO服务器的URL
access-key: dxywjy_minio_test # 访问密钥
secret-key: 89^Njo5poG#IJove # 密钥密码
bucket-name: one-phone-tour # 默认的Bucket名称
secure: false # 是否使用HTTPS连接
创建 Minio 配置类
@Configuration
@EnableConfigurationProperties(MinioConfig.MinioProp.class)
public class Config{
//单例的 只有一个
@Bean
public MinioClient minioClient(){
//这里还可以设置一些其他的 例如 创建一个信任所有证书的TrustManager 、 创建一个自定义的OkHttpClient
return MinioClient.builder()
.endpoint(minioProp.endpoint) //minioProp 指代配置文件中的相关配置
.credentials(minioProp.accessKey, minioProp.secretKey)
.build();
}
@Data
@ConfigurationProperties("minio")
public static class MinioProp {
private String endpoint;
private String accessKey;
private String secretKey;
private String bucketName = "default";
}
}
Minio 是单例的,所有操作都依靠一个客户端MinioClient,那么他是否有线程安全问题?
没有线程安全的问题
原因:
- MinioClient 在内部对共享资源(如网络连接、配置信息等)进行了合理的管理。当多个线程同时调用 MinioClient 的方法(如文件上传、下载等操作)时,它会通过同步机制来确保这些操作不会相互干扰。例如,在底层网络通信部分,可能会使用锁(Lock)机制或者其他并发控制原语来保证在同一时刻只有一个线程可以访问和修改网络连接相关的状态。
- MinioClient 通常会使用 HTTP 连接池来管理与 Minio 服务器的连接。连接池本身是线程安全的设计。它可以有效地复用连接,减少连接建立和关闭的开销。在多线程环境下,连接池会根据一定的规则(如最大连接数、空闲连接超时等)来分配和回收连接。例如,当一个线程完成文件上传操作并释放连接后,连接池会将该连接标记为空闲状态,其他线程可以在需要时复用这个连接,并且连接池会确保多个线程不会同时对同一个连接进行不适当的操作。
- MinioClient 的某些操作可能是无状态的,这意味着这些操作不依赖于之前的操作状态,每个操作都是独立的。对于有状态的操作,MinioClient 会进行妥善的状态管理。例如,在文件上传的过程中,它会记录上传的进度、已上传的数据块等信息,这些状态信息的更新和访问是在一个线程安全的方式下进行的,以避免不同线程之间的冲突。
Minio 中的 Bucket、Object
Bucket: 是存储 Object 的逻辑空间,每个 Bucket 之间的数据是相互隔离的,对用户而言,相当于存放文件的顶层文件夹(目录);
Object: 是存储到 Minio 的基本对象,对用户而言,相当于文件;
MinioClient Minio客户端常用 API:
对存储桶 Bucket 的操作:
bucketExists() //用于检查指定的存储桶是否存在,返回布尔值,表示存储桶是否存在;
public void test() throws Exception { //需要给 bucketExists() 方法 传一个BucketExistsArgs参数
boolean result = minioClient.bucketExists(BucketExistsArgs.builder().bucket("桶名").build());
//存在 true 不存在 false
}
makeBucket() //用于创建一个新的存储桶(bucket),需要指定存储桶的名称;
public void test() throws Exception {
minioClient.makeBucket(MakeBucketArgs.builder.bucket("桶名").build()); //无返回值
//我们还可以给存储桶设置 权限
String policyJsonString = 访问策略,到时候需要自己gpt
minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
.bucket("桶名")
.config(policyJsonString)
.build()
);
}
listBuckets() //用于列出用户有权访问的所有存储桶,返回存储桶的列表;
public void test(){
List<Bucket> list = minioClient.listBuckets();
list.forEach(bucket -> {
System.out.println(bucket.name() + "----" + bucket.creationDate());
})
}
removeBucket() //用于删除一个已经存在的存储桶(bucket),删除失败会抛出异常
public void test(){
// 报异常 删除失败,没有报异常,删除成功
minioClient.removeBucket(RemoveBucketArgs.builder().bucket("桶名").build());
}
对文件 Object 的操作:
putObject() //用于上传文件到指定的存储桶;
方式一:
public void test(){
File file = new File("文件路径")
//通过检查ObjectWriteResponse是否为null来初步判断上传操作是否成功。
ObjectWriteResponse objectWriteResponse= minioClient.putObject(PutObjectArgs.builder()
.bucket("桶名")
.object("指定存储到minio 文件后,在minio显示的文件名")
//参数一:文件的输入流 参数二 文件大小 参数三 partsize 分块上传
.stream(new FileInputStream(file) , file.length() ,-1)
);
}
方式二:
public void test(){
//多次上传同一个文件的话,而且 Object 名字一样的话 会覆盖掉原来的
ObjectWriteResponse objectWriteResponse = minioClient.uploadObject(UploadObjectArgs.builder()
.bucket("桶名")
.Object("存到minio的文件名")
.filename("要存到minio的文件路径")
.build()
);
}
isObjectExists() //用于检查指定的对象(文件)是否存在于指定的存储桶中; 过期 目前用 StatObject()
public void test(){
//找到会返回对象信息,包括文件所在的桶,文件名,大小
//找不到会报错
StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder()
.bucket("桶名")
.object("要找的文件,在minio中的名字")
.build()
);
}
getObjectUrl() //用于生成一个对象(文件) 的 URL ,以便可以通过HTTP直接访问
8.5.9版本没有这个方法了,我们可以用 getPresignedObjectUrl( GetPresignedObjectUrlArgs args)
//这个方法呢表名我们的url 不可以随便进行访问,需要有签名
//如果想要不加后面的签名 就要访问minio中的文件,在客户端把桶bucket的权限改为 public 就可以了
public void test(){
String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.bucket("桶名")
.object("在minio中的文件名")
.expiry(3,TimeUnit.MINUTES) //可选的, 这个url 3分钟有效
.method(Method.POST) //只支持post请求
.build()
);
}
getObject() //用于从指定的存储桶中下载文件
public void test(){
GetObjectResponse response = minioClient.getObject(GetObjectArgs.builder()
.bucket("桶名")
.object("minio中要查的文件名")
.build()
);
//response对象中有一个输入流,我们可以通过 transferTo(new FileOutputStream("图片路径"))方法,将文件下载到该路径;
response.transferTo(new FileOutPutStream("文件路径"));
}
listObject() //用于列出指定存储桶中的所有对像(文件)
public void test(){
Iterable<Result<item>> listObjects = minioClient.listObjects(ListObjectsArgs.builder()
.bucket("存储桶名")
.build()
);
listObject.forEach(item -> {
try{
Item item = item.get(); //item 就是文件对象,里面存储信息
} catch (Exception e){
throw new RuntimeException(e)
}
})
}
removeObject() //用于删除指定存储桶中的对象,需要指定存储桶名称和对象键
public void test(){
// 没有返回值,删除失败会报异常
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket("桶名")
.object("要删除的文件,minio中的文件名")
.build()
);
}