MinIO是一款高性能、分布式的开源对象存储系统, 它是一款软件产品。 MinIO公司旨在解决非结构化数据增长的需求, 开发了流行于业界的开源云存储软件MinIO。
虽然MinIO是100%开源的, 但它既是一家公司又是一个开源项目。它采用GNU AGPL v3开源证书, 拥有GNU AGPL代码的版权, 同时还是MinIO项目的主要贡献者, 可独立对MinIO进行维护。
1.分布式对象存储: MinIO 是一个高度可扩展的对象存储系统, 旨在存储大规模的非结构化数据, 如图像、视频、文档等。它将数据以对象的形式存储在分布式集群中。
2.高度可扩展: MinIO 可以轻松地扩展以适应不断增长的存储需求。您可以通过添加新的存储节点来增加存储容量和性能, 从而实现横向扩展。
3.高性能: MinIO 针对读取和写入对象进行了优化,具有出色的性能。它采用并发处理和多线程等技术来提高吞吐量, 使其非常适合需要快速存取大量对象的应用。
4.S3兼容: MinIO 提供了与 Amazon S3 兼容的 API, 这意味着您可以使用现有的 S3 客户端和工具来与 MinIO 进行交互。这种兼容性使得迁移现有 S3 数据和应用程序变得容易。
5.安全性: MinIO 提供了多种安全性功能, 包括数据加密、访问控制列表 (ACLs)、策略管理和身份验证, 以确保存储的数据得到保护。
6.开源和免费: MinIO 是开源软件, 根据Apache License 2.0许可证发布, 可以免费使用和修改。
1.数据类型: MinIO 主要用于存储非结构化数据, 如文件、图片和视频等二进制数据。关系型数据库, 用于存储结构化数据, 例如表格数据。
2.数据模型: MinIO 使用对象存储模型, 将数据存储为对象, 每个对象可以包含元数据和二进制数据。关系型数据库使用表格模型, 数据以表格的形式进行组织。
3.用途: MinIO 通常用于存储大规模的文件和媒体数据, 用于构建数据湖、媒体存储、备份和归档等场景。关系型数据库主要用于事务性应用程序, 如电子商务、金融系统和业务应用的数据存储。
4.性能和扩展性: MinIO 针对大规模对象存储进行了优化, 支持高并发和横向扩展。MySQL 针对事务性操作进行了优化, 不同于对象存储的性能和扩展性需求。
Object: 存储到 Minio 的基本对象, 如文件、字节流, 视频、音频、日志、镜像等等
Bucket: 用来存储 Object 的逻辑空间。每个 Bucket 之间的数据是相互隔离的。对于客户端而言, 就相当于一个存放文件的顶层文件夹。
Drive: 即存储数据的磁盘,在 MinIO 启动时,以参数的方式传入。Minio 中所有的对象数据都会存储在Drive里。
Set: 即一组 Drive 的集合, 分布式部署根据集群规模自动划分一个或多个Set, 每个Set中的 Drive 分布在不同位置。
创建桶时指定域的作用
Minio桶的法定区域是一个可选项,它用于指定桶中对象的数据存储位置。这对于遵守数据存储的法规和合规性非常重要。
当你在创建Minio桶时指定了域,实际上是在告诉Minio系统将该桶的数据存储在特定的地理区域内。这有助于确保你的数据存储符合特定国家或地区的法规和政策。这也可能涉及到数据本地化的需求,即确保数据存储在特定的地理位置,以满足法规或组织内部政策的要求。
桶的概念理解
MinIO中的桶(Bucket)是一种顶层容器,用于组织和存储对象(Object),而桶本身是不能嵌套的。MinIO的桶结构是扁平的, 也就是说MinIO不支持在一个桶内创建子桶或嵌套桶。
如果你需要对对象进行更细粒度的组织,可以使用对象键(Object Key)的命名约定,通常使用斜杠(/)或其他字符来模拟目录结构,以实现对象的逻辑组织。例如,你可以创建类似于"folder/object"的键,来模拟文件夹结构,但实际上这只是使用键来模拟目录结构,而不是实际的嵌套桶。
<dependency>
<groupId>io.miniogroupId>
<artifactId>minioartifactId>
<version>8.3.5version>
dependency>
import io.minio.MinioClient;
public class MinioExample {
public static void main(String[] args) throws Exception {
String serverUrl = "http://localhost:9000";
String accessKey = "your-access-key";
String secretKey = "your-secret-key";
MinioClient minioClient = MinioClient.builder()
.endpoint(serverUrl)
.credentials(accessKey, secretKey)
.build();
//构造方法二
MinioClient minioClient =
MinioClient.builder()
.endpoint("https://localhost:9000")
.credentials("Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG")
.region("eu-east-1")
.httpClient(customHttpClient)
.build();
//构造方式三
MinioClient minioClient = new MinioClient("https://play.min.io", "Q3AM3UQ867SPQQA43P2F", "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG");
}
}
判断桶是否存在
boolean found = minioClient.bucketExists("mybucket");
创建桶
minioClient.makeBucket("mybucket");
//创建桶指定区域
if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket("my-new-bucket").build())) {
minioClient.makeBucket(
MakeBucketArgs.builder().bucket("my-new-bucket").region("new-bucket-west-1").build());
}
//创建桶,加入锁
//用于确保数据的不可变性和合规性,尤其适用于需要长期数据保留和数据安全性的应用程序和环境
if (!minioClient.bucketExists("my-new-bucket-with-object-lock")) {
minioClient.makeBucket(
MakeBucketArgs.builder()
.bucket("my-new-bucket-with-object-lock")
.region("new-bucket-west-1")
.objectLock(true)
.build());
}
列出所有的桶
List<Bucket> bucketList = minioClient.listBuckets();
for (Bucket bucket : bucketList) {
System.out.println(bucket.creationDate() + ", " + bucket.name());
}
//列出某个桶中的所有对象
boolean found = minioClient.bucketExists("mybucket");
if (found) {
Iterable<Result<Item>> myObjects = minioClient.listObjects("mybucket");
for (Result<Item> result : myObjects) {
Item item = result.get();
System.out.println(item.lastModified() + ", " + item.size() + ", " + item.objectName());
}}
//列出存储桶中被部分上传的对象
//listIncompleteUploads(String bucketName, String prefix, boolean recursive)
//bucketName 存储桶名称; prefix 对象名称的前缀; recursizve 是否递归查找;
boolean found = minioClient.bucketExists("mybucket");
if (found) {
Iterable<Result<Upload>> myObjects = minioClient.listIncompleteUploads("mybucket");
for (Result<Upload> result : myObjects) {
Upload upload = result.get();
System.out.println(upload.uploadId() + ", " + upload.objectName());
}
删除桶
boolean found = minioClient.bucketExists("mybucket");
if (found) {
minioClient.removeBucket("mybucket");
}
获取和设置策略
//获得指定对象前缀的存储桶策略
minioClient.getBucketPolicy("myBucket", "downloads");
//给一个桶添加策略
minioClient.setBucketPolicy("myBucket", "uploads", PolicyType.READ_ONLY);
以流的形式下载一个对象
try {
// 调用statObject()来判断对象是否存在, 不存在抛异常
minioClient.statObject("mybucket", "myobject");
// 获取流
InputStream stream = minioClient.getObject("mybucket", "myobject");
byte[] buf = new byte[16384];
int bytesRead;
while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) {
System.out.println(new String(buf, 0, bytesRead));
}
// 关闭流
stream.close();
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
}
下载对象指定区域的字节数组做为流(断点下载)
try {
// 判断对象是否存在, 不存在抛异常
minioClient.statObject("mybucket", "myobject");
// 获取指定offset和length的"myobject"的输入流
InputStream stream = minioClient.getObject("mybucket", "myobject", 1024L, 4096L);
// 读取输入流直到EOF并打印到控制台。
byte[] buf = new byte[16384];
int bytesRead;
while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) {
System.out.println(new String(buf, 0, bytesRead));
}
// 关闭流。
stream.close();
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
}
下载并将文件保存到本地
try {
// 不存在 抛异常
minioClient.statObject("mybucket", "myobject");
// 获取myobject的流并保存到photo.jpg文件中。
minioClient.getObject("mybucket", "myobject", "photo.jpg");
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
}
对加密数据进行解密下载(AES加密)
try {
// 判断是否存在 不存在抛异常
minioClient.statObject("mybucket", "myobject");
//生成256位AES key。
KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES");
symKeyGenerator.init(256);
SecretKey symKey = symKeyGenerator.generateKey();
// 获取对象数据并保存到photo.jpg
InputStream stream = minioClient.getObject("testbucket", "my-objectname", symKey);
// 读流到EOF,并输出到控制台。
byte[] buf = new byte[16384];
int bytesRead;
while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) {
System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8));
}
// 关闭流。
stream.close();
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
}
对加密数据进行解密下载(RSA加密)
try {
//文件不存在则抛异常
minioClient.statObject("mybucket", "myobject");
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
keyGenerator.initialize(1024, new SecureRandom());
KeyPair keypair = keyGenerator.generateKeyPair();
// 获取对象数据并保存到photo.jpg
InputStream stream = minioClient.getObject("testbucket", "my-objectname", keypair);
// 读流到EOF,并输出到控制台。
byte[] buf = new byte[16384];
int bytesRead;
while ((bytesRead = stream.read(buf, 0, buf.length)) >= 0) {
System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8));
}
// 关闭流。
stream.close();
} catch (MinioException e) {
System.out.println("Error occurred: " + e);
}
通过InputStream上传对象
单个对象的最大大小限制在5TB。putObject在对象大于5MiB时,自动使用multiple parts方式上传。
这样,当上传失败时,客户端只需要上传未成功的部分即可(类似断点上传)。上传的对象使用MD5SUM签名进行完整性验证。
try {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 1000; i++) {
builder.append("Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. ");
builder.append("(29 letters)\n");
builder.append("Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. ");
builder.append("- --\n");
}
ByteArrayInputStream bais = new ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
// 创建对象
minioClient.putObject("mybucket", "myobject", bais, bais.available(), "application/octet-stream");
bais.close();
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}
通过文件上传到对象中
try {
minioClient.putObject("mybucket", "island.jpg", "/mnt/photos/island.jpg")
System.out.println("island.jpg is uploaded successfully");
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}
AES加密上传
try {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 1000; i++) {
builder.append("Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. ");
builder.append("(29 letters)\n");
builder.append("Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. ");
builder.append("(31 letters)\n");
builder.append("- --\n");
}
ByteArrayInputStream bais = new
ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
//生成256位AES key.
KeyGenerator symKeyGenerator = KeyGenerator.getInstance("AES");
symKeyGenerator.init(256);
SecretKey symKey = symKeyGenerator.generateKey();
// 创建一个对象
minioClient.putObject("mybucket", "myobject", bais, bais.available(), "application/octet-stream", symKey);
bais.close();
System.out.println("myobject is uploaded successfully");
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}
RSA加密上传
try {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 1000; i++) {
builder.append("Sphinx of black quartz, judge my vow: Used by Adobe InDesign to display font samples. ");
builder.append("(29 letters)\n");
builder.append("Jackdaws love my big sphinx of quartz: Similarly, used by Windows XP for some fonts. ");
builder.append("(31 letters)\n");
builder.append("- --\n");
}
ByteArrayInputStream bais = new
ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("RSA");
keyGenerator.initialize(1024, new SecureRandom());
KeyPair keypair = keyGenerator.generateKeyPair();
// Create an object
minioClient.putObject("mybucket", "myobject", bais, bais.available(), "application/octet-stream", keypair);
bais.close();
System.out.println("myobject is uploaded successfully");
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}
获取对象的元数据
try {
// 获得对象的元数据。
ObjectStat objectStat = minioClient.statObject("mybucket", "myobject");
System.out.println(objectStat);
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}
从objectName指定的对象中将数据拷贝到destObjectName指定的对象
try {
CopyConditions copyConditions = new CopyConditions();
copyConditions.setMatchETagNone("TestETag");
minioClient.copyObject("mybucket", "island.jpg", "mydestbucket", "processed.png", copyConditions);
System.out.println("island.jpg is uploaded successfully");
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}
删除一个对象
try {
// 从mybucket中删除myobject。
minioClient.removeObject("mybucket", "myobject");
System.out.println("successfully removed mybucket/myobject");
} catch (MinioException e) {
System.out.println("Error: " + e);
}
删除多个对象
List<String> objectNames = new LinkedList<String>();
objectNames.add("my-objectname1");
objectNames.add("my-objectname2");
objectNames.add("my-objectname3");
try {
// 删除my-bucketname里的多个对象
for (Result<DeleteError> errorResult: minioClient.removeObject("my-bucketname", objectNames)) {
DeleteError error = errorResult.get();
System.out.println("Failed to remove '" + error.objectName() + "'. Error:" + error.message());
}
} catch (MinioException e) {
System.out.println("Error: " + e);
}
删除一个未完整上传的对象
try {
// 从存储桶中删除名为myobject的未完整上传的对象。
minioClient.removeIncompleteUpload("mybucket", "myobject");
System.out.println("successfully removed all incomplete upload session of my-bucketname/my-objectname");
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}
生成一个给HTTP GET请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
try {
String url = minioClient.presignedGetObject("mybucket", "myobject", 60 * 60 * 24);
System.out.println(url);
} catch(MinioException e) {
System.out.println("Error occurred: " + e);
}
允许给POST请求的presigned URL设置策略,比如接收对象上传的存储桶名称的策略,key名称前缀,过期策略。
try {
PostPolicy policy = new PostPolicy("mybucket", "myobject",
DateTime.now().plusDays(7));
policy.setContentType("image/png");
Map<String,String> formData = minioClient.presignedPostPolicy(policy);
System.out.print("curl -X POST ");
for (Map.Entry<String,String> entry : formData.entrySet()) {
System.out.print(" -F " + entry.getKey() + "=" + entry.getValue());
}
System.out.println(" -F file=@/tmp/userpic.png https://play.min.io/mybucket");
} catch(MinioException e) {
System.out.println("Error occurred: " + e);