1 前言
1.1 官方文档和SDK
官方文档:https://min.io/docs/minio/kubernetes/upstream/index.html?ref=docs-redirect
SDK:https://github.com/minio/minio-java
Minio 提供了多种语言的SDK,比如java、go、python等。JAVA开发平台可以选择JS和java SDK,也就是前端和后端都可以直接集成minio。
1.2 技术方案
每个OSS的用户都会用到上传服务。Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS。具体流程如下图所示。
和数据直传到OSS相比,以上方法有三个缺点:
-
上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
-
扩展性差:如果后续用户多了,应用服务器会成为瓶颈。
-
费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。
目前通过Web前端技术上传文件到OSS,有三种技术方案:
-
利用OSS js SDK将文件上传到OSS,也就是前端直连OSS,但是容易暴露认证信息,安全性不太高。
-
使用表单上传方式,将文件上传到OSS。利用OSS提供的接口临时接口,使用表单上传方式将文件上传到OSS。然后请求后端,告知上传完成,进行后续处理。
-
先上传到应用服务器,再请求OSS上传,这种安全性较高,可以对数据和认证进行管控,但是性能最差。
2 集成 JAVA SDK
因为一般的非互联网项目,对性能要求不高,所以采用JAVA SDK集成MInio,然后提供接口给Web端调用就行了。
2.1 环境搭建
首先搭建一个Maven基础工程,引入相关依赖,这里引入的是最新的8.3.1版本。还引入了okhttp的最新包,不然某些API会提示版本太低。
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.3.1</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.2</version>
</dependency>
2.2 初始化客户端
可以看到现在minio都是采用Builder构建者模式来构造对象,和之前有很大的区别,所以需要注意。
//url为地址,accessKey和secretKey为用户名和密码
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
2.3 存储桶基础操作
2.3.1 存储桶是否存在
检查存储桶是否存在。
public boolean bucketExists(BucketExistsArgs args)
示例代码:
/**
* 判断桶是否存在
*/
public static boolean bucketExists(String url, String accessKey, String secretKey, String bucketName)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}
2.3.2 创建存储桶
创建一个启用给定区域和对象锁定功能的存储桶。
public void makeBucket(MakeBucketArgs args)
示例代码:
/**
* 添加存储桶
*/
public static void makeBucket(String url, String accessKey, String secretKey, String bucketName, String region, boolean objectLock)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).objectLock(objectLock).build());
}
创建后,就可以在控制台看到这些存储桶了,最后那个被锁定的存储桶,上传文件及删除后,发现还是会显示存在这些对象,实际磁盘上的文件并没有删除
2.3.3 查询存储桶信息列表
列出所有桶的桶信息。
public List<Bucket> listBuckets()
示例代码:
/**
* 查询存储桶信息列表
*/
public static List<Bucket> listBuckets(String url, String accessKey, String secretKey) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
return minioClient.listBuckets();
}
public static void main(String[] args) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
List<Bucket> buckets = listBuckets("url", "accessKey", "secretKey");
for (Bucket bucket : buckets) {
System.out.println(bucket.creationDate() + ", " + bucket.name());
}
}
打印信息如下,返回的创建时间是美国时间,需要注意。
2.3.4 删除存储桶
删除一个空桶。
public void removeBucket(RemoveBucketArgs args)
示例代码:
/**
* 删除存储桶
*/
public static void removeBucket(String url, String accessKey, String secretKey, String bucketName)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
注意:要确保存储桶存在,否则会报错,删除时最好调用bucketExists()方法判断是否存在
2.4 设置存储桶操作
2.4.1 加密配置
设置桶的加密配置,以允许在该桶中上传对象时,采用对应加密配置对数据进行加密。当前支持配置的服务端加密方式为KMS托管密钥的服务端加密(SSE-KMS),及AES256加密。
设置桶的加密配置:
public void setBucketEncryption(SetBucketEncryptionArgs args)
获取桶的加密配置:
public SseConfiguration getBucketEncryption(GetBucketEncryptionArgs args)
2.4.2 生命周期
生命周期管理可适用于以下典型场景:
-
周期性上传的日志文件,可能只需要保留一个星期或一个月。到期后要删除它们。
-
某些文档在一段时间内经常访问,但是超过一定时间后便可能不再访问了。这些文档需要在一定时间后转化为低频访问存储,归档存储或者删除。
存储桶生命周期配置:
public void setBucketLifecycle(SetBucketLifecycleArgs args)
获取桶的生命周期配置:
public LifecycleConfiguration getBucketLifecycle(GetBucketLifecycleArgs args)
示例代码:
// 5. 生命周期
List<LifecycleRule> rules = new LinkedList<>();
// 配置生命周期规则
rules.add(
new LifecycleRule(
Status.ENABLED, // 开启状态
null,
new Expiration((ZonedDateTime) null, 365, null), // 保存365天
new RuleFilter("logs/"), // 目录配置
"rule2",
null,
null,
null));
LifecycleConfiguration lifecycleConfiguration = new LifecycleConfiguration(rules);
// 添加生命周期配置
minioClient.setBucketLifecycle(
SetBucketLifecycleArgs.builder().bucket("my-bucketname").config(lifecycleConfiguration).build());
// 获取配置
LifecycleConfiguration lifecycleConfiguration1111 =
minioClient.getBucketLifecycle(
GetBucketLifecycleArgs.builder().bucket("my-bucketname").build());
List<LifecycleRule> rules1 = lifecycleConfiguration1111.rules();
for (int i = 0; i < rules1.size(); i++) {
System.out.println("Lifecycle status is " + rules1.get(i).status());
System.out.println("Lifecycle prefix is " + rules1.get(i).filter().prefix());
System.out.println("Lifecycle expiration days is " + rules1.get(i).expiration().days());
}
打印结果如下:
2.4.3 通知配置
可以使用存储桶事件通知来监控存储桶中对象上发生的事件。
MinIO 服务器支持的各种事件类型有:
存储桶配置通知:
public void setBucketPolicy(SetBucketPolicyArgs args)
获取桶的通知配置:
public NotificationConfiguration getBucketNotification(GetBucketNotificationArgs args)
代码示例:
// 6. 通知配置
// Add a new SQS configuration.
NotificationConfiguration notificationConfiguration = new NotificationConfiguration();
List<QueueConfiguration> queueConfigurationList = notificationConfiguration.queueConfigurationList();
QueueConfiguration queueConfiguration = new QueueConfiguration();
queueConfiguration.setQueue("arn:minio:sqs::1:webhook");
List<EventType> eventList = new LinkedList<>();
eventList.add(EventType.OBJECT_CREATED_PUT);
eventList.add(EventType.OBJECT_CREATED_COPY);
queueConfiguration.setEvents(eventList);
queueConfiguration.setPrefixRule("images");
queueConfiguration.setSuffixRule("pg");
queueConfigurationList.add(queueConfiguration);
notificationConfiguration.setQueueConfigurationList(queueConfigurationList);
// Set updated notification configuration.
minioClient.setBucketNotification(
SetBucketNotificationArgs.builder().bucket("my-bucketname").config(notificationConfiguration).build());
System.out.println("Bucket notification is set successfully");
NotificationConfiguration minioClientBucketNotification =
minioClient.getBucketNotification(
GetBucketNotificationArgs.builder().bucket("my-bucketname").build());
System.out.println(minioClientBucketNotification);
2.4.4 策略配置
添加存储桶策略配置。
public void setBucketPolicy(SetBucketPolicyArgs args)
获取桶的桶策略配置。
public String getBucketPolicy(GetBucketPolicyArgs args)
2.4.5 复制配置
存储桶复制旨在将存储桶中的选定对象复制到目标存储桶,内容较多,后续补上
添加存储桶的复制配置
public void setBucketReplication(SetBucketReplicationArgs args)
获取桶的桶复制配置:
public ReplicationConfiguration getBucketReplication(GetBucketReplicationArgs args)
2.4.6 存储桶标签
当为桶添加标签时,该桶上所有请求产生的计费话单里都会带上这些标签,从而可以针对话单报表做分类筛选,进行更详细的成本分析。例如:某个应用程序在运行过程会往桶里上传数据,我们可以用应用名称作为标签,设置到被使用的桶上。在分析话单时,就可以通过应用名称的标签来分析此应用的成本。
setBucketTags可以为存储桶设置标签。
public void setBucketTags(SetBucketTagsArgs args)
getBucketTags获取桶的标签。
public Tags getBucketTags(GetBucketTagsArgs args)
示例代码:
// 1. 存储桶标签
Map<String, String> map = new HashMap<>();
map.put("Project", "Project One");
map.put("User", "jsmith");
// 设置标签
minioClient.setBucketTags(SetBucketTagsArgs.builder().bucket("my-bucketname").tags(map).build());
// 查询标签
Tags bucketTags = minioClient.getBucketTags(GetBucketTagsArgs.builder().bucket("my-bucketname").build());
System.out.println(bucketTags.get().toString());
返回结果:
2.4.7 多版本设置
若开启了多版本控制,上传对象时,OBS自动为每个对象创建唯一的版本号。上传同名的对象将以不同的版本号同时保存在OBS中。
若未开启多版本控制,向同一个文件夹中上传同名的对象时,新上传的对象将覆盖原有的对象。
某些功能(例如版本控制、对象锁定和存储桶复制)需要使用擦除编码分布式部署 MinIO。开启了版本控制后,允许在同一密钥下保留同一对象的多个版本。
设置存储桶的版本控制配置。
public void setBucketVersioning(SetBucketVersioningArgs args)
获取存储桶的版本控制配置。
public VersioningConfiguration getBucketVersioning(GetBucketVersioningArgs args)
代码示例:
// 2. 版本配置
// 'my-bucketname'启用版本控制
minioClient.setBucketVersioning(
SetBucketVersioningArgs.builder()
.bucket("my-bucketname")
.config(new VersioningConfiguration(VersioningConfiguration.Status.ENABLED, null))
.build());
System.out.println("Bucket versioning is enabled successfully");
// 'my-bucketname'暂停版本控制
minioClient.setBucketVersioning(
SetBucketVersioningArgs.builder()
.bucket("my-bucketname")
.config(new VersioningConfiguration(VersioningConfiguration.Status.SUSPENDED, null))
.build());
System.out.println("Bucket versioning is suspended successfully");
2.4.8 对象锁定配置
对象锁定设置后,删除对象后,会仍然存在磁盘中。
在存储桶中设置对象锁定配置。
public void setObjectLockConfiguration(SetObjectLockConfigurationArgs args)
获取存储桶中的对象锁配置。
public ObjectLockConfiguration getObjectLockConfiguration(GetObjectLockConfigurationArgs args)
需要先设置存储桶为对象锁定模式,示例代码:
// 3. 将保留模式设置为Compliance,且持续时间为100天
// 设置锁定对象的保留模式及时限
ObjectLockConfiguration config =
new ObjectLockConfiguration(RetentionMode.COMPLIANCE, new RetentionDurationDays(100));
minioClient.setObjectLockConfiguration(
SetObjectLockConfigurationArgs.builder()
.bucket("my-bucketname-in-eu-with-object-lock")
.config(config)
.build());
System.out.println("object-lock configuration is set successfully");
// 获取锁定配置
ObjectLockConfiguration objectLockConfiguration =
minioClient.getObjectLockConfiguration(
GetObjectLockConfigurationArgs.builder()
.bucket("my-lock-enabled-bucketname")
.build());
System.out.println("Object-lock configuration of bucket");
System.out.println("Mode: " + objectLockConfiguration.mode());
System.out.println("Duration: " + objectLockConfiguration.duration());
2.5 删除配置
minio提供了一些列的delete方法用于删除配置,比较简单,就不举例说明了。
2.5.1 删除桶的加密配置
public void deleteBucketEncryption(DeleteBucketEncryptionArgs args)
2.5.2 删除存储桶的生命周期配置
public void deleteBucketLifecycle(DeleteBucketLifecycleArgs args)
2.5.3 删除桶的标签
public void deleteBucketTags(DeleteBucketTagsArgs args)
2.5.4 删除桶的桶策略配置
public void deleteBucketPolicy(DeleteBucketPolicyArgs args)
2.5.5 删除存储桶的存储桶复制配置
public void deleteBucketReplication(DeleteBucketReplicationArgs args)
2.5.6 删除桶的通知配置
public void deleteBucketNotification(DeleteBucketNotificationArgs args)
3 相关工具类
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
/**
* minio工具类
*
* @author wuKeFan
* @date 2023-09-08 14:08:10
*/
public class MinioUtil {
/**
* 判断桶是否存在
*/
public static boolean bucketExists(String url, String accessKey, String secretKey, String bucketName)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}
/**
* 添加存储桶
*/
public static void makeBucket(String url, String accessKey, String secretKey, String bucketName, String region, boolean objectLock)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).objectLock(objectLock).build());
}
/**
* 指定地区添加存储桶
*/
public static void makeBucket(String url, String accessKey, String secretKey, String bucketName)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
/**
* 指定地区添加存储桶并锁定对象
*/
public static void makeBucket(String url, String accessKey, String secretKey, String bucketName, String region)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region(region).build());
}
/**
* 删除存储桶
*/
public static void removeBucket(String url, String accessKey, String secretKey, String bucketName)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
/**
* 设置桶公有
*/
public static void setBucketPublicPolicy(String url, String accessKey, String secretKey, String bucketName)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
String sb = "{\"Version\":\"2012-10-17\"," +
"\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":" +
"{\"AWS\":[\"*\"]},\"Action\":[\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"," +
"\"s3:GetBucketLocation\"],\"Resource\":[\"arn:aws:s3:::" + bucketName +
"\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:PutObject\",\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\"],\"Resource\":[\"arn:aws:s3:::" +
bucketName +
"/*\"]}]}";
minioClient.setBucketPolicy(
SetBucketPolicyArgs.builder()
.bucket(bucketName)
.config(sb)
.build());
}
/**
* 设置桶私有
*/
public static void setBucketPrivatePolicy(String url, String accessKey, String secretKey, String bucketName)
throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
minioClient.setBucketPolicy(
SetBucketPolicyArgs.builder().bucket(bucketName)
.config(
"{\"Version\":\"2012-10-17\",\"Statement\":[]}"
)
.build());
}
/**
* 查询存储桶信息列表
*/
public static List<Bucket> listBuckets(String url, String accessKey, String secretKey) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
MinioClient minioClient = MinioClient.builder().endpoint(url)
.credentials(accessKey, secretKey).build();
return minioClient.listBuckets();
}
}