MinIO对象存储——Java SDK

Java
475
0
0
2024-02-24

系列文章:

对象存储——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并下载获取。