MinIO说明

MinIO是一款高性能、分布式的开源对象存储系统, 它是一款软件产品。 MinIO公司旨在解决非结构化数据增长的需求, 开发了流行于业界的开源云存储软件MinIO。
虽然MinIO是100%开源的, 但它既是一家公司又是一个开源项目。它采用GNU AGPL v3开源证书, 拥有GNU AGPL代码的版权, 同时还是MinIO项目的主要贡献者, 可独立对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许可证发布, 可以免费使用和修改。

MinIO和常规数据库的区别

1.数据类型: MinIO 主要用于存储非结构化数据, 如文件、图片和视频等二进制数据。关系型数据库, 用于存储结构化数据, 例如表格数据。
2.数据模型: MinIO 使用对象存储模型, 将数据存储为对象, 每个对象可以包含元数据和二进制数据。关系型数据库使用表格模型, 数据以表格的形式进行组织。
3.用途: MinIO 通常用于存储大规模的文件和媒体数据, 用于构建数据湖、媒体存储、备份和归档等场景。关系型数据库主要用于事务性应用程序, 如电子商务、金融系统和业务应用的数据存储。
4.性能和扩展性: MinIO 针对大规模对象存储进行了优化, 支持高并发和横向扩展。MySQL 针对事务性操作进行了优化, 不同于对象存储的性能和扩展性需求。

MinIO中的基本概念

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"的键,来模拟文件夹结构,但实际上这只是使用键来模拟目录结构,而不是实际的嵌套桶。

Java中调用MinIO

使用Maven添加依赖

<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.3.5</version>
</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);
}

Presigned操作

生成一个给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);
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐