系列文章:
对象存储——Minio 初探
一 MinIO SDK
对象存储——Minio 初探中我们介绍了单机部署MinIO的过程,以及在控制台上的一些操作。因为在实际应用中,主要还是通过sdk进行操作,所以这里我们也开始介绍MinIO SDK的使用。MinIO SDK的官网地址:https://min.io/docs/minio/linux/developers/minio-drivers.html?ref=docs。从中可以看出,MinIO发布了.NET、Golang、Haskell、Java、JavaScript、Python共6种语言的SDK,接下来我们基于Java SDK来实现对MinIO的一些常规操作。
二 Java SDK
2.1 当前版本
截止目前(2023.12.22),Java SDK的版本为8.5.7,github地址:minio/minio-java
与其他依赖相同,支持maven、gradle活jar包直接引入方式。我们以Maven为例:
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.7</version>
</dependency>
2.2 api示例-文件上传
2.2.1 准备信息
这是一个官方示例。上传文件到minio服务器需要以下三个参数:
Endpoint :S3 服务的Url
Access Key:minio账号的ak.
Secret Key:minio的sk.
ak、sk创建/查询方法,可参考如下截图,在Service Accounts下,点击Create service account 按钮创建账号。
在这个页面上,复制Access Key 和 Secret Key保存。
2.2.2 官方测试服务信息
注意,如果本地还没有搭建minio服务,可以先使用官方文档提供的两个测试服务地址:
MinIO:
MinioClient minioClient =
MinioClient.builder()
.endpoint("https://play.min.io")
.credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG")
.build();
AWS S3:
MinioClient minioClient =
MinioClient.builder()
.endpoint("https://s3.amazonaws.com")
.credentials("YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY")
.build();
2.2.3 文件上传代码示例
package com.freemark.demo.minio;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.UploadObjectArgs;
import io.minio.errors.MinioException;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
public class FileUploader {
/**
* 这里配置自己的endpoint和ak sk
*/
public static String endPoint = "http://你的minio server ip:端口号";
public static String accessKey = "你的ak";
public static String secretKey = "你的sk";
public static void main(String[] args)
throws IOException, NoSuchAlgorithmException, InvalidKeyException {
try {
// 创建minioClient,使用官方提供的示例地址和ak sk
MinioClient minioClient =
MinioClient.builder()
.endpoint("https://play.min.io")
.credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG")
.build();
// 如果指定的bucket不存在,则创建,否则使用已有bucket
String bucketName = "asiatrip1";
boolean found =
minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!found) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
} else {
System.out.println("Bucket '" + bucketName + "' already exists.");
}
String filePath = "/Users/lijingyong/Downloads/minio_test_text.txt";
String fileName = "minio_test_text.txt";
// 执行文件上传
minioClient.uploadObject(
UploadObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.filename(filePath)
.build());
System.out.println("'" + filePath + "' 成功上传对象 '" + fileName + "' 到 bucket '" + bucketName + "'.");
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
System.out.println("HTTP trace: " + e.httpTrace());
}
}
}
执行后打印如下信息:
如果使用的是自己搭建的minio服务地址及对应的ak和sk,那么我们就能够看到在指定的bucket下有我们刚刚上传的文件。如下所示:
如果执行多次,会发现提示bucket已存在,不会重复创建,但文件会多次上传,覆盖上传。
2.3 完整API说明
其他完整的API,我们也可以查看官方文档:https://min.io/docs/minio/linux/developers/java/API.html#bucketExists。下面以可能会经常用到的文件下载、删除完成本篇示例,其他可以查看文档,这里就不在赘述。
2.4 文件下载
2.4.1 文件下载
方法名:downloadObject
入参:DownloadObjectArgs,参数列表:
bucket: 要从哪个bucket下载文件
object: 要下载的对象名
filename:要保存的文件路径
使用如下代码,即可执行对指定bucket下指定对象的下载,下载的文件会保存在filename参数对应的路径下。
minioClient.downloadObject( DownloadObjectArgs.builder() .bucket("my-bucketname") .object("my-objectname") .filename("my-object-file") .build());
如果服务端文件进行了加密,那我们在下载时也需要增加ssec参数来做支持,
ServerSideEncryptionCustomerKey ssec =
new ServerSideEncryptionCustomerKey(
new SecretKeySpec(
"01234567890123456789012345678901".getBytes(StandardCharsets.UTF_8), "AES"));
minioClient.downloadObject(
DownloadObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.filename("/Users/xxx/Downloads/download_minio_test_text.txt")
.ssec(ssec)
.build());
System.out.println(fileName + "下载完毕");
注意,如果我们使用的minio没有安装成带有TLS的服务,那么执行上述代码会报如下错误。
Minio服务端加密是大部分文章都没有提到的,所以这里我们稍微展开说明一下。
2.5 Minio服务端加密
参考Minio Cookbook 中文版 中的如何使用aws-cli调用Minio服务端加密,Minio支持采用客户端提供的秘钥(SSE-C)进行S3服务端加密。客户端必须为SSE-C请求指定三个HTTP请求头:
1、算法标识符: X-Amz-Server-Side-Encryption-Customer-Algorithm唯一的合法值是: AES256。
2、加密秘钥: X-Amz-Server-Side-Encryption-Customer-Key加密秘钥必须是一个256位的base64编码的字符串。
3、加密密钥MD5校验和: X-Amz-Server-Side-Encryption-Customer-Key-MD5加密密钥MD5校验和必须是秘钥的MD5和,注意是原始秘钥的MD5和,而不是base64编码之后的。
2.5.1 安全须知
- 根据S3规范,minio服务器将拒绝任何通过不安全(非TLS)连接进行的SSE-C请求。这意味着SSE-C必须是TLS / HTTPS。
- SSE-C请求包含加密密钥。如果通过非TLS连接进行SSE-C请求,则必须将SSE-C加密密钥视为受损。
- 根据S3规范,SSE-C PUT操作返回的content-md5与上传对象的MD5-sum不匹配。
- Minio Server使用防篡改加密方案来加密对象,并且不会保存加密密钥。这意味着您有责任保管好加密密钥。如果你丢失了某个对象的加密密钥,你将会丢失该对象。
- Minio Server期望SSE-C加密密钥是高熵的。加密密钥是不是密码。如果你想使用密码,请确保使用诸如Argon2,scrypt或PBKDF2的基于密码的密钥派生函数(PBKDF)来派生高熵密钥。
2.5.2 前提条件
minio安装时需要安装成带有TLS的服务。从这里下载Minio Server,安装方式参考文档Network Encryption (TLS) 生成证书。
这里需要注意的是,如果你使用的是自己签名的TLS证书,那么当你往Minio Server上传文件时,像aws-cli或者是mc这些工具就会报错。如果你想获得一个CA结构签名的TLS证书,请参考Let's Encrypt
。自己签名的证书应该仅做为内部开发和测试。
2.5.3 使用SSE-C和aws-cli
从这里下载并安装aws-cli。
假设你在本地运行了一个Minio Server,地址是https://localhost:9000
,并且使用的是自己签名的证书。为了绕过TLS证书的验证,你需要指定—no-verify-ssl
。如果你的Minio Server使用的是一个CA认证的证书,那你永远永远永远不要指定`—no-verify-ssl,否则aws-cli会接受任何证书。
2.6 其他SDK
2.6.1 对象删除
使用removeObject方法,支持删除单个对象、
// Remove object.
minioClient.removeObject(
RemoveObjectArgs.builder().bucket("my-bucketname").
object("my-objectname").build());
删除指定的对象的某个版本:
// Remove versioned object.
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket("my-bucketname")
.object("my-versioned-objectname")
.versionId("my-versionid")
.build());
绕过治理模式删除版本控制的对象:
// Remove versioned object bypassing Governance mode.
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket("my-bucketname")
.object("my-versioned-objectname")
.versionId("my-versionid")
.bypassRetentionMode(true)
.build());
2.6.2 对象批量删除
使用removeObjects,参数为对象List:
List<DeleteObject> objects = new LinkedList<>();
objects.add(new DeleteObject("my-objectname1"));
objects.add(new DeleteObject("my-objectname2"));
objects.add(new DeleteObject("my-objectname3"));
Iterable<Result<DeleteError>> results =
minioClient.removeObjects(
RemoveObjectsArgs.builder().bucket("my-bucketname").objects(objects).build());
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
System.out.println(
"Error in deleting object " + error.objectName() + "; " + error.message());
}
2.6.3 其他api
其他常用的API还包括获取bucket下的文件列表:listObjects,设置bucket生命周期:setBucketLifecycle等等。因为官方API文档和其他文章提供的工具类中都有描述,这里就不再赘述了。工具类代码大家可以查看https://gitee.com/flamingskyline/template-engine并下载获取。