Minio概念

MinIO is an object storage solution that provides an Amazon Web Services S3-compatible API and supports all core S3 features. MinIO is built to deploy anywhere - public or private cloud, baremetal infrastructure, orchestrated environments, and edge infrastructure.

Minio搭建

在linux服务器上执行

mkdir -p /blessing/minio/data
mkdir -p /blessing/minio/config
docker run -dit  \
   --restart always \
   -p 9000:9000 \
   -p 9090:9090 \
   --name minio \
   -v  /blessing/minio/data:/data \
   -v  /blessing/minio/config:/root/.minio \
   -e "MINIO_ROOT_USER=DawnSilverGravel" \
   -e "MINIO_ROOT_PASSWORD=DawnSilverGravel" \
   quay.io/minio/minio:RELEASE.2023-06-02T23-17-26Z.fips  server /data --console-address ":9090" --address ":9000"

使用docker启动参数具体在参考文档的第二个链接,此时RedHat Mminio镜像仓库没有最新的latest,故使用RELEASE.2023-06-02T23-17-26Z.fips版本。

如果没有docker,同样在参考文档的第二链接,然后点击Linux标签页找到安装命令,在Minio版本可以找到对应的版本,以下是下载minio二进制的命令。

mkdir -p /blessing/minio/data
# 查看liunx系统架构,如果是x86_64就是AMD的
# 下载minio二进制文件
wget https://dl.min.io/server/minio/release/linux-amd64/minio -P /blessing/minio
# 赋予权限
cd /blessing/minio
chmod +x minio
# 启动minio --console-address控制台端口,默认是9001,默认上传端口是9000
# 账号密码默认:minioadmin/minioadmin
MINIO_ROOT_USER=DawnSilverGravel MINIO_ROOT_PASSWORD=DawnSilverGravel ./minio server /blessing/minio/data --console-address ":9090"

启动之后在浏览器访问http://yourAddress:9090 在Access Keys页面生成密钥得到access-keysecret-key

Minio使用

Java API

在官方的文档中(下方参考文档第一个链接),以及MinioClient类中,和下方参考文档的第三个链接中,都可以找到相关方法的示例,这些方法参数都有一个共同的特点,使用了Builder模式,从中也使用了Consumer<T>函数式接口,可以学习一下其思想。

bucket method描述
bucketExists检查bucket是否存在
listBuckets获取所有bucket的信息
makeBucket创建给定区域和对象锁特性的bucket
removeBucket删除一个无数据的bucket
getBucketTags获取bucket标签
setBucketTags设置bucket标签,之前的tags会被删除
deleteBucketTags删除bucket标签
getBucketVersioning获取bucket版本
setBucketVersioning设置bucket版本(版本默认是无,设置了之后就只能配置EnabledSuspended)
getObjectLockConfigurationArgs获取bucket的对象锁配置,makeBucket新建bucketobjectLock参数为true
setObjectLockConfigurationArgs设置bucket对象锁配置,makeBucket新建bucketobjectLock参数为true,限制对对象的修改
deleteObjectLockConfigurationArgs删除bucket对象锁配置makeBucket新建bucketobjectLock参数为true
......其余暂不了解
object method描述
putObject以流的形式上传对象
uploadObject以文件的形式上传对象
copyObject创建一个对象并复制minio服务器上的另一个对象的数据,两个对象不能相同
composeObject组合minio服务器端不同的源对象来创建一个对象,每个对象大小不可以低于5M
uploadSnowballObjects在单个put中上传多个文件,SnowballObject中size与指定的流大小要一致
removeObject删除一个对象
removeObjects删除多个对象
statObject获取一个对象的对象信息和元数据
getObject获取对象数据,并返回InputStream流;,该流必须关闭以释放网络资源
getPresignedObjectUrl获取一个对象的http方法、过期时间、自定义参数的URL,GetPresignedObjectUrlArgsGETPUTHEAD三种类型的http方法
downloadObject以本地文件的形式下载一个对象,DownloadObjectArgs中的fileName中文件名如果本地存在将会下载失败
getPresignedPostFormData获取一个对象PostPolicy的表单数据的POST方法URL,然后使用该URL上传文件
listObjects获取一个bucket里对象列表
getObjectTags获取对象的标签
setObjectTags设置对象标签
deleteObjectTags删除对象标签
getObjectRetention获取对象保留策略配置,指定创建bucket创建时需要设置objectLocktrue
setObjectRetention设置对象保留策略配置,指定创建bucket创建时需要设置objectLocktrue
enableObjectLegalHold启用对对象的合法保留,默认为false,上传对象时可设置,指定bucket创建时要设置objectLocktrue
disableObjectLegalHold禁用对对象的合法保留,指定bucket创建时设置objectLocktrue
isObjectLegalHoldEnabled返回对象是否启用合法保留,启用为true,指定bucket创建时要设置objectLocktrue
selectObjectContent通过SQL表达式选择对象的内容(不知如何使用)
restoreObject归档一个对象(不知如何使用)

PutObjectArgs为例

minio没有文件夹的概念,但是可以使用object(/blessing/yourFileName)可以创建一个blessing文件夹

针对于putObject方法中的参数对象PutObjectArgs 中的stream方法有其中两个参数objectSizepartSize,有以下描述:

  • 允许最大对象为5TB
  • 允许最小的分片为5M
  • 允许最大分片为5GB(这里注释看了一下代码应该是写错了,版本8.5.3
  • 最大的分片数量为10000
  • 从图中可以得知这是一个父类,则下图中的子类同样有上述规则。

    项目结构与配置

    pom.xml

    <dependencies>
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.5.3</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.15</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.12</version>
        </dependency>
    </dependencies>
    

    application.yml

    minio:
      init-buckets:
        - dawn-silver-gravel
        - blessing-star
      operation-bucket: dawn-star
      access-key: yourAccessKey
      secret-key: yourSecretKey
      # 默认端口9000
      endpoint: http://yourAddress:yourPort
    

    config配置

    package com.example.config;
    import io.minio.MinioClient;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import javax.annotation.Resource;
     * description:
     * @author DawnStar
     * date: 2023/6/14
    @Configuration
    @EnableConfigurationProperties(MinioProperties.class)
    public class MinioConfiguration {
        @Resource
        private MinioProperties minioProperties;
        @Bean
        public MinioClient minioClient() {
            return MinioClient.builder().credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
                    .endpoint(minioProperties.getEndpoint())
                    .build();
    
    package com.example.config;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
     * description:
     * @author DawnStar
     * date: 2023/6/14
    @ConfigurationProperties(prefix = "minio")
    @Component
    public class MinioProperties {
        private String accessKey;
        private String secretKey;
        private String endpoint;
        private String[] initBuckets;
        private String operationBucket;
        public String getOperationBucket() {
            return operationBucket;
        public void setOperationBucket(String operationBucket) {
            this.operationBucket = operationBucket;
        public String[] getInitBuckets() {
            return initBuckets;
        public void setInitBuckets(String[] initBuckets) {
            this.initBuckets = initBuckets;
        public String getAccessKey() {
            return accessKey;
        public void setAccessKey(String accessKey) {
            this.accessKey = accessKey;
        public String getSecretKey() {
            return secretKey;
        public void setSecretKey(String secretKey) {
            this.secretKey = secretKey;
        public String getEndpoint() {
            return endpoint;
        public void setEndpoint(String endpoint) {
            this.endpoint = endpoint;
    

    文件工具类

    package com.example.util;
    import java.io.*;
    import java.nio.file.Files;
    import java.util.Random;
    import java.util.concurrent.ThreadLocalRandom;
     * description:
     * 生成文件工具类
     * @author DawnStar
     * date: 2023/6/15
    public class MinioFileUtils {
        private final static ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
        public static InputStream createFile(String fileName) {
            File file = new File(fileName);
            try (BufferedWriter bufferedWriter = new BufferedWriter
    
    
    
    
        
    (
                    new OutputStreamWriter(Files.newOutputStream(file.toPath())))) {
                bufferedWriter.write("这是一个测试文件");
                return Files.newInputStream(file.toPath());
            } catch (IOException e) {
                throw new RuntimeException(e);
        public static File getUploadFile(String fileName) {
            File file = new File(fileName);
            try (BufferedWriter bufferedWriter = new BufferedWriter(
                    new OutputStreamWriter(Files.newOutputStream(file.toPath())))) {
                bufferedWriter.write(generateContent(fileName));
                return file;
            } catch (IOException e) {
                throw new RuntimeException(e);
        public static String generateContent(String fileName) {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(fileName).append("\n");
            int anInt = RANDOM.nextInt(300, 500);
            int lineLength = 50;
            for (int i = 0; i < anInt; i++) {
                for (int i1 = 0; i1 < lineLength; i1++) {
                    char c = (char) RANDOM.nextInt(30, 122);
                    stringBuilder.append(c);
                stringBuilder.append("\n");
            return stringBuilder.toString();
        public static String generateContentPartSize(String fileName,long partSize) {
            if (partSize < 5 * 1024 * 1024) {
                throw new IllegalArgumentException("非法参数");
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(fileName).append("\n");
            int lineLength = 50;
            long l = partSize / 50 + 1;
            for (long i = 0; i < l; i++) {
                for (int i1 = 0; i1 < lineLength; i1++) {
                    char c = (char) RANDOM.nextInt(30, 122);
                    stringBuilder.append(c);
                stringBuilder.append("\n");
            return stringBuilder.toString();
    

    Minio 部分Java API 使用

    MinioBucketApi
    package com.example.template;
    import io.minio.*;
    import io.minio.messages.*;
    import lombok.SneakyThrows;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    import javax.annotation.Resource;
    import java.util.HashMap;
    import java.util.List;
     * description:
     * minio 方法测试
     * @author DawnStar
     * date: 2023/6/15
    @Component
    public class MinioBucketApi {
        @Resource
        private MinioClient minioClient;
        @Value("${minio.operation-bucket}")
        private String buckName;
         * 桶标签操作
        @SneakyThrows
        public void bucketTagsOperation() {
            System.out.println("***********************桶标签操作*******************************");
            Tags tags = Tags.newBucketTags(new HashMap<String, String>(2) {{
                this.put("blessing", "star");
                this.put("images", "image");
            SetBucketTagsArgs setBucketTagsArgs = SetBucketTagsArgs.builder().bucket(buckName).tags(tags).build();
            minioClient.setBucketTags(setBucketTagsArgs);
            System.out.println("================获取桶标签========================");
            GetBucketTagsArgs getBucketTagsArgs = GetBucketTagsArgs.builder().bucket(buckName).build();
            Tags bucketTags = minioClient.getBucketTags(getBucketTagsArgs);
            System.out.println(buckName + "标签:" + bucketTags.get());
            System.out.println("================设置桶标签========================");
            SetBucketTagsArgs newSetBucketTagsArgs = SetBucketTagsArgs.builder().bucket(buckName).tags(new HashMap<String, String>(1) {{
                this.put("newTag", "newTag");
            }}).build();
            minioClient.setBucketTags(newSetBucketTagsArgs);
            Tags bucketTags1 = minioClient.getBucketTags(getBucketTagsArgs);
            System.out.println(buckName + "标签:" + bucketTags1.get());
            System.out.println("================删除桶标签=======================");
            DeleteBucketTagsArgs deleteBucketTagsArgs = DeleteBucketTagsArgs.builder().bucket(buckName).build();
            minioClient.deleteBucketTags(deleteBucketTagsArgs);
            Tags newTags = minioClient.getBucketTags(getBucketTagsArgs);
            System.out.println(buckName + "标签:" + newTags.get());
            System.out.println("***********************************************************************************");
         * 桶版本操作
        @SneakyThrows
        public void bucketVersionOperation() {
            System.out.println("***********************桶版本操作*******************************");
            System.err.println("桶版本默认没有,开启了就只有两种状态:Enable 和 Suspended状态");
            System.out.println("=================获取桶版本===================");
            GetBucketVersioningArgs getBucketVersioningArgs = GetBucketVersioningArgs.builder().bucket(buckName).build();
            VersioningConfiguration bucketVersioning = minioClient.getBucketVersioning(getBucketVersioningArgs);
            System.out.println(buckName + "版本:" + bucketVersioning.status().toString() + bucketVersioning.isMfaDeleteEnabled());
            System.out.println("===================设置桶版本==================");
            VersioningConfiguration.Status enabled = VersioningConfiguration.Status.ENABLED;
            VersioningConfiguration configuration = new VersioningConfiguration(enabled, true);
            SetBucketVersioningArgs setBucketVersioningArgs = SetBucketVersioningArgs.builder().bucket(buckName).config(configuration).build();
            minioClient.setBucketVersioning(setBucketVersioningArgs);
            bucketVersioning = minioClient.getBucketVersioning(getBucketVersioningArgs);
            System.out.println(buckName + "版本:" + bucketVersioning.status().toString() + bucketVersioning.isMfaDeleteEnabled());
            System.out.println("***********************************************************************************");
         * 桶基本操作
        @SneakyThrows
        public void bucketBasicOperation() {
            System.out.println("***********************桶基本操作*******************************");
            System.out.println("===============检查桶是否存在========================");
            BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(buckName).build();
            boolean bucketExists = minioClient.bucketExists(bucketExistsArgs);
            System.out.println(buckName + "存在:" + bucketExists);
            // 获取桶列表
            System.out.println("=================获取桶列表======================");
            List<Bucket> buckets = minioClient.listBuckets();
            buckets.forEach(bucket -> System.out.println(bucket.name()));
            System.out.println("=================新建桶============================");
            MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket("test").objectLock(true).build();
            // 创建已有bucket会创建失败
            minioClient.makeBucket(makeBucketArgs);
            buckets = minioClient.listBuckets();
            buckets.forEach(bucket -> System.out.println(bucket.name()));
            // 删除桶,有文件就会删除失败
            System.out.println("=================删除桶======================");
            RemoveBucketArgs removeBucketArgs = RemoveBucketArgs.builder().bucket("test").build();
            minioClient.removeBucket(removeBucketArgs);
            buckets = minioClient.listBuckets();
            buckets.forEach(bucket -> System.out.println(bucket.name()));
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void objectLockConfigurationOperation() {
            System.out.println("***********************桶对象锁配置*******************************");
            GetObjectLockConfigurationArgs getObjectLockConfigurationArgs = GetObjectLockConfigurationArgs.builder().bucket(buckName).build();
            ObjectLockConfiguration objectLockConfiguration = minioClient.getObjectLockConfiguration(getObjectLockConfigurationArgs);
            System.out.println("获取当前的bucket对象锁配置"+objectLockConfiguration.mode() + " " + objectLockConfiguration.duration());
            objectLockConfiguration = new ObjectLockConfiguration(RetentionMode.COMPLIANCE, new RetentionDurationDays(1));
            minioClient.setObjectLockConfiguration(SetObjectLockConfigurationArgs.builder().bucket(buckName).config(objectLockConfiguration).build());
            objectLockConfiguration = minioClient.getObjectLockConfiguration(getObjectLockConfigurationArgs);
            System.out.println("设置后的bucket对象锁配置"+objectLockConfiguration.mode() + " " + objectLockConfiguration.duration());
            minioClient.deleteObjectLockConfiguration(DeleteObjectLockConfigurationArgs.builder().bucket(buckName).build());
            objectLockConfiguration = minioClient.getObjectLockConfiguration(getObjectLockConfigurationArgs);
            System.out.println("删除桶锁对象配置:"+objectLockConfiguration.mode() + " " + objectLockConfiguration.duration());
            System.out.println("***********************************************************************************");
    
    MinioObjectApi
    package com.example.template;
    import cn.hutool.http.ContentType;
    import cn.hutool.http.HttpRequest;
    import cn.hutool.http.HttpResponse;
    import cn.hutool.http.HttpUtil;
    import com.example.util.MinioFileUtils;
    import io.minio.*;
    import io.minio.http.Method;
    import io.minio.messages.*;
    import lombok.SneakyThrows;
    import okhttp3.*;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    import javax.annotation.Resource;
    import
    
    
    
    
        
     java.io.*;
    import java.nio.charset.StandardCharsets;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.TimeUnit;
     * description:
     * @author DawnStar
     * date: 2023/6/16
    @Component
    public class MinioObjectApi {
        @Resource
        private MinioClient minioClient;
        @Value("${minio.operation-bucket}")
        private String buckName;
        @SneakyThrows
        public void putObject() {
            // 上传已知的大小的文件
            System.out.println("************************************putObject操作***********************************************");
            String fileName = "test.txt";
            InputStream inputStream = MinioFileUtils.createFile(fileName);
            System.out.println("==================上传已知的大小的文件==========================");
            PutObjectArgs firstObjectArgs = PutObjectArgs.builder()
                    .bucket(buckName)
                    .object(fileName)
                    // -1 表示默认分片为 5M
                    .stream(inputStream, inputStream.available(), -1).build();
            ObjectWriteResponse objectWriteResponse = minioClient.putObject(firstObjectArgs);
            System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
            // 上传未知大小的文件
            System.out.println("==================上传未知的大小的文件==========================");
            PutObjectArgs secondObjectArgs = PutObjectArgs.builder()
                    .bucket(buckName)
                    .object(fileName)
                    .stream(inputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
                    .build();
            objectWriteResponse = minioClient.putObject(secondObjectArgs);
            System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
            // 上传未知大小的文件
            System.out.println("==================上传内存中的数据==========================");
            String content = "这是一个内存运行的数据";
            Map<String, String> headers = new HashMap<>();
            headers.put("X-Amz-Storage-Class", "REDUCED_REDUNDANCY");
            Map<String, String> userMetadata = new HashMap<>();
            byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentBytes);
            PutObjectArgs thirdObjectArgs = PutObjectArgs.builder()
                    .bucket(buckName)
                    // 将创建一个名为putMethod文件夹
                    .object("putMethod/content.txt")
                    // 两者不能同时为-1
                    .stream(byteArrayInputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
                    .headers(headers)
                    .userMetadata(userMetadata)
                    .build();
            objectWriteResponse = minioClient.putObject(thirdObjectArgs);
            System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void uploadObject() {
            System.out.println("************************************uploadObject操作***********************************************");
            String fileName = "uploadObject.txt";
            File file = MinioFileUtils.getUploadFile(fileName);
            UploadObjectArgs uploadObjectArgs = UploadObjectArgs.builder()
                    .bucket(buckName)
                    .object(fileName)
    //                .filename(file.toPath())
                    .filename(file.toPath().toString(), UploadObjectArgs.MAX_PART_SIZE)
                    .build();
            ObjectWriteResponse objectWriteResponse = minioClient.uploadObject(uploadObjectArgs);
            System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void copyObject() {
            String fileName = "copy.txt";
            putObjectByPartSize("", fileName);
            System.out.println("************************************copyObject操作***********************************************");
            // object() 与 source()不可相同
            CopyObjectArgs copyObjectArgs = CopyObjectArgs.builder()
                    .bucket(buckName)
                    .object("copyMethod/" + fileName)
                    .source(CopySource.builder().bucket(buckName).object(fileName).build())
                    .build();
            ObjectWriteResponse objectWriteResponse = minioClient.copyObject(copyObjectArgs);
            System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void composeObject() {
            System.out.println("************************************composeObject操作***********************************************");
            List<ComposeSource> composeSources = new ArrayList<>();
            String prefix = "subComposeObject";
            for (int i = 0; i < 2; i++) {
                String composeFileName = prefix + i + ".txt";
                putObjectByPartSize("composeMethod", composeFileName);
                ComposeSource composeSource = ComposeSource.builder()
                        .bucket(buckName)
                        .object("composeMethod/" + composeFileName)
                        .build();
                composeSources.add(composeSource);
            String fileName = "composeObject.txt";
            ComposeObjectArgs composeObjectArgs = ComposeObjectArgs.builder()
                    .bucket(buckName)
                    .object("composeMethod/" + fileName)
                    // 每个文件大小不可以低于5M
                    .sources(composeSources)
                    .build();
            ObjectWriteResponse objectWriteResponse = minioClient.composeObject(composeObjectArgs);
            System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void uploadSnowballObjects() {
            System.out.println("************************************uploadSnowballObjects操作***********************************************");
            String prefix = "uploadSnowballMethod";
            List<SnowballObject> objects = new ArrayList<>();
            // 参数中size必须与文件大小相等
            String content = MinioFileUtils.generateContent(prefix + "my-object-one");
            byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
            InputStream inputStream = MinioFileUtils.createFile(prefix + "my-object-two");
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentBytes);
            objects.add(
                    new SnowballObject(
                            prefix + "/my-object-one",
                            byteArrayInputStream,
                            byteArrayInputStream.available(),
                            null));
            objects.add(
                    new SnowballObject(
                            prefix + "/my-object-two",
                            inputStream,
                            inputStream.available(),
                            null));
            ObjectWriteResponse objectWriteResponse = minioClient.uploadSnowballObjects(
                    UploadSnowballObjectsArgs.builder().bucket(buckName).objects(objects).build());
            System.out.println(objectWriteResponse.versionId() + " " + objectWriteResponse.etag());
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void removeObject() {
            System.out.println("************************************removeObject与removeObjects操作***********************************************");
            System.out.println("====================删除单个对象================================");
            RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(buckName).object("test").build();
            // 删除不存在的对象也不会有问题
            minioClient.removeObject(removeObjectArgs);
            DeleteObject deleteObject = new DeleteObject("test");
            System.out.println("====================删除多个对象================================");
            String prefix = "removeMethod";
            DeleteObject deleteObject1 = new DeleteObject(prefix + "/removeObject.txt");
            putObjectByPartSize(prefix, "removeObject.txt");
            RemoveObjectsArgs removeObjectsArgs = RemoveObjectsArgs.builder().bucket(buckName).objects(new ArrayList<>() {{
                this.add(deleteObject);
                this.add(deleteObject1);
            }}).build();
            Iterable<Result<DeleteError>> results = minioClient.removeObjects(removeObjectsArgs);
            // 删除不存在的对象也不会有问题
            for (Result<DeleteError> deleteErrorResult : results) {
                System.out.println(deleteErrorResult.get().toString());
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void statObject() {
            System.out.println("************************************statObjects操作***********************************************");
            uploadObject("statMethod", "statObject.txt");
            StatObjectArgs statObjectArgs = StatObjectArgs.builder()
                    .bucket(buckName)
                    .object("statMethod/statObject.txt")
                    .build();
            StatObjectResponse statObjectResponse = minioClient.statObject(statObjectArgs);
            System.out.println("statObject:" + statObjectResponse.etag() + " " + statObjectResponse.object() + " " + statObjectResponse.versionId());
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void getObject() {
            System.out.println("************************************getObjects操作***********************************************"
    
    
    
    
        
    );
            uploadObject("getMethod", "getObject.txt");
            GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                    .bucket(buckName)
                    .object("getMethod/getObject.txt")
                    // 起始位置
                    .offset(10L)
                    // 数据长度
                    .length(100L)
                    .build();
            GetObjectResponse response = minioClient.getObject(getObjectArgs);
            byte[] bytes = response.readAllBytes();
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(bytes.length);
            System.out.println(byteArrayOutputStream);
            byteArrayOutputStream.close();
            response.close();
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void getPresignedObjectUrl() {
            System.out.println("************************************getPresignedObjectUrl操作***********************************************");
            GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
                    .bucket(buckName)
                    .object("getPresignedMethod/notGetPresignedObjectUrl.txt")
                    .expiry(24, TimeUnit.HOURS)
                    .method(Method.GET)
                    .build();
            String presignedObjectUrl = minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
            System.out.println("没有该文件的路径" + presignedObjectUrl);
            // 获取上传路径
            GetPresignedObjectUrlArgs putPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
                    .bucket(buckName)
                    .object("getPresignedMethod/getPresignedObjectUrl.txt")
                    .expiry(24, TimeUnit.HOURS)
                    .method(Method.PUT)
                    .build();
            presignedObjectUrl = minioClient.getPresignedObjectUrl(putPresignedObjectUrlArgs);
            System.out.println("PUT方法上传路径:" + presignedObjectUrl);
            // 上传数据
            HttpRequest request = HttpUtil.createRequest(cn.hutool.http.Method.PUT, presignedObjectUrl);
            byte[] bytes = MinioFileUtils.generateContent("getPresignedObjectUrl.txt").getBytes(StandardCharsets.UTF_8);
            HttpResponse execute = request.body(bytes).contentType(ContentType.TEXT_PLAIN.getValue()).execute();
            execute.close();
            GetPresignedObjectUrlArgs targetPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
                    .bucket(buckName)
                    .object("getPresignedMethod/getPresignedObjectUrl.txt")
                    .expiry(24, TimeUnit.HOURS)
                    .method(Method.GET)
                    .build();
            presignedObjectUrl = minioClient.getPresignedObjectUrl(targetPresignedObjectUrlArgs);
            System.out.println("获取路径:" + presignedObjectUrl);
            GetPresignedObjectUrlArgs headPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
                    .bucket(buckName)
                    .object("getPresignedMethod/getPresignedObjectUrl.txt")
                    .method(Method.HEAD)
                    .build();
            presignedObjectUrl = minioClient.getPresignedObjectUrl(headPresignedObjectUrlArgs);
            System.out.println("HEAD方法路径,请通过postman请求该路径:" + presignedObjectUrl);
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void downLoadObject() {
            System.out.println("************************************downLoadObject操作***********************************************");
            uploadObject("downloadMethod", "downloadObject.txt");
            DownloadObjectArgs downloadObjectArgs = DownloadObjectArgs.builder()
                    .bucket(buckName)
                    .object("downloadMethod/downloadObject.txt")
                    .filename("downloadObject.txt")
                    .build();
            // 文件名存在将会下载失败
            System.err.println("文件名存在将会下载失败");
            minioClient.downloadObject(downloadObjectArgs);
            File file = new File("downloadObject.txt");
            BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
            String readLine;
            while ((readLine = bufferedReader.readLine()) != null) {
                System.out.print(readLine);
            bufferedReader.close();
            System.out.println("***********************************************************************************");
        @Value("${minio.endpoint}")
        private String url;
        @SneakyThrows
        public void getPresignedPostFormData() {
            System.out.println("************************************getPresignedPostFormData操作***********************************************");
            // 创建一个时效为7天的策略
            PostPolicy policy = new PostPolicy(buckName, ZonedDateTime.now().plusDays(7));
            String objectName = "PostFormMethod/postFormData.txt";
            // 添加条件,`key`键为对象名称
            policy.addEqualsCondition("key", objectName);
            // 添加'Content-Type.设置前缀为“application”
            policy.addStartsWithCondition("Content-Type", "application/");
            // 添加内容长度范围,20Bit - 1MiB,不在这个区间的文件上传失败
            policy.addContentLengthRangeCondition(20, 1024*1024);
            Map<String, String> formData = minioClient.getPresignedPostFormData(policy);
            // 以表单的形式上传文本
            MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();
            multipartBuilder.setType(MultipartBody.FORM);
            for (Map.Entry<String, String> entry : formData.entrySet()) {
                multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue());
            multipartBuilder.addFormDataPart("key", objectName);
            multipartBuilder.addFormDataPart("Content-Type",ContentType.OCTET_STREAM.getValue());
            String fileName = objectName.substring(objectName.lastIndexOf("/") + 1);
            File uploadFile = MinioFileUtils.getUploadFile(fileName);
            // "file" 必须最后添加
            multipartBuilder.addFormDataPart(
                    "file", objectName, RequestBody.create(uploadFile, null));
            System.out.println(url+"/"+objectName);
            Request request =
                    new Request.Builder()
                            // minio服务器路径
                            .url(url +"/"+buckName)
                            .post(multipartBuilder.build())
                            .build();
            OkHttpClient httpClient = new OkHttpClient().newBuilder().build();
            try (Response response = httpClient.newCall(request).execute();) {
                if (response.isSuccessful()) {
                    System.out.println(fileName + " is uploaded successfully using POST object");
                } else {
                    System.out.println("Failed to upload " + fileName);
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void listObjects() {
            System.out.println("************************************listObjects操作***********************************************");
            uploadObject("listMethod", "listObject1.txt");
            uploadObject("listMethod", "listObject2.txt");
            uploadObject("listMethod", "listObject3.txt");
            System.out.println("===================第一种List==================");
            ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder().bucket(buckName).build();
            Iterable<Result<Item>> listObjects = minioClient.listObjects(listObjectsArgs);
            print(listObjects);
            System.out.println("===================第二种List==================");
            listObjectsArgs = ListObjectsArgs.builder()
                    .bucket(buckName)
                    .recursive(true)
                    .build();
            print(minioClient.listObjects(listObjectsArgs));
            System.out.println("===================第三种List==================");
            listObjectsArgs = ListObjectsArgs.builder()
                    .bucket(buckName)
                    .recursive(true)
                    .prefix("listMethod")
                    .build();
            print(minioClient.listObjects(listObjectsArgs));
            System.out.println("===================第四种List==================");
            listObjectsArgs = ListObjectsArgs.builder()
                    .bucket(buckName)
                    .recursive(true)
                    .prefix("listMethod")
                    .startAfter("listMethod/listObject2.txt")
                    .build();
            print(minioClient.listObjects(listObjectsArgs));
            // ......
            System.out.println("***********************************************************************************");
        private void print(Iterable<Result<Item>> listObjects) throws Exception {
            for (Result<Item> listObject : listObjects) {
                System.out.println(listObject.get().objectName());
        @SneakyThrows
        public void objectRetentionOperation() {
            System.out.println("************************************对象保留策略操作***********************************************");
            System.err.println("创建bucket需要设置objectLock为true");
            uploadObject("retentionMethod", "objectRetention.txt");
            GetObjectRetentionArgs getObjectRetentionArgs = GetObjectRetentionArgs.builder().bucket(buckName).object("retentionMethod/objectRetention.txt")
                    .build();
            Retention objectRetention = minioClient.getObjectRetention(getObjectRetentionArgs);
            if (objectRetention == null) {
                System.out.println("修改策略前配置: null");
            } else {
                System.out.println("修改策略前配置: " + objectRetention.mode().toString() + " " + objectRetention.retainUntilDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            Retention retention = new Retention(RetentionMode.COMPLIANCE, ZonedDateTime.now().plusSeconds(40));
            SetObjectRetentionArgs setObjectRetentionArgs = SetObjectRetentionArgs.builder()
                    .bucket(buckName)
                    .object("retentionMethod/objectRetention.txt")
                    .config(retention)
                    // ByPass Mode(略过模式或旁路模式),泛指在一个系统的正常流程中,
                    // 有一堆检核机制,而“ByPass Mode”就是当检核机制发生异常,
                    // 无法在短期间内排除时,使系统作业能绕过这些检核机制,
                    // 使系统能够继续运行的作业模式
                    .bypassGovernanceMode(true)
                    .build();
            minioClient.setObjectRetention(setObjectRetentionArgs);
            objectRetention = minioClient.getObjectRetention(getObjectRetentionArgs);
            System.out.println("修改策略后配置:" + objectRetention.mode().toString() + " " + objectRetention.retainUntilDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void objectLegalHoldOperation
    
    
    
    
        
    () {
            System.out.println("************************************对象合法保留操作***********************************************");
            System.err.println("创建bucket需要设置objectLock为true");
            uploadObject("legalHoldMethod", "objectLegalHoldMethod.txt");
            IsObjectLegalHoldEnabledArgs isObjectLegalHoldEnabledArgs = IsObjectLegalHoldEnabledArgs.builder()
                    .bucket(buckName)
                    .object("legalHoldMethod/objectLegalHoldMethod.txt")
                    .build();
            boolean objectLegalHoldEnabled = minioClient.isObjectLegalHoldEnabled(isObjectLegalHoldEnabledArgs);
            System.out.println("执行isObjectLegalHoldEnabledArgs:" + objectLegalHoldEnabled);
            EnableObjectLegalHoldArgs enableObjectLegalHoldArgs = EnableObjectLegalHoldArgs.builder()
                    .bucket(buckName)
                    .object("legalHoldMethod/objectLegalHoldMethod.txt")
                    .build();
            minioClient.enableObjectLegalHold(enableObjectLegalHoldArgs);
            objectLegalHoldEnabled = minioClient.isObjectLegalHoldEnabled(isObjectLegalHoldEnabledArgs);
            System.out.println("执行enableObjectLegalHold:" + objectLegalHoldEnabled);
            DisableObjectLegalHoldArgs disableObjectLegalHoldArgs = DisableObjectLegalHoldArgs.builder()
                    .bucket(buckName)
                    .object("legalHoldMethod/objectLegalHoldMethod.txt")
                    .build();
            minioClient.disableObjectLegalHold(disableObjectLegalHoldArgs);
            objectLegalHoldEnabled = minioClient.isObjectLegalHoldEnabled(isObjectLegalHoldEnabledArgs);
            System.out.println("执行disableObjectLegalHold:" + objectLegalHoldEnabled);
            System.out.println("***********************************************************************************");
        @SneakyThrows
        public void objectTagOperation() {
            System.out.println("************************************对象标签操作***********************************************");
            uploadObject("tagMethod", "objectTags.txt");
            // 与给bucket添加标签的方法是相似的
            SetObjectTagsArgs setObjectTagsArgs = SetObjectTagsArgs.builder()
                    .bucket(buckName)
                    .object("tagMethod/objectTags.txt")
                    .tags(new HashMap<String, String>(1) {{
                        this.put("blessing", "star");
                    .build();
            minioClient.setObjectTags(setObjectTagsArgs);
            GetObjectTagsArgs getObjectTagsArgs = GetObjectTagsArgs.builder()
                    .bucket(buckName)
                    .object("tagMethod/objectTags.txt")
                    .build();
            Tags objectTags = minioClient.getObjectTags(getObjectTagsArgs);
            System.out.println("设置后的标签:" + objectTags.get());
            DeleteObjectTagsArgs deleteObjectTagsArgs = DeleteObjectTagsArgs.builder()
                    .bucket(buckName)
                    .object("tagMethod/objectTags.txt")
                    .build();
            minioClient.deleteObjectTags(deleteObjectTagsArgs);
            objectTags = minioClient.getObjectTags(getObjectTagsArgs);
            System.out.println("删除后的标签对象:" + objectTags.get());
            System.out.println("***********************************************************************************");
    //    @SneakyThrows
    //    public void selectObjectContent() {
    //        uploadObject("selectMethod", "selectObjectContent1.txt");
    //        uploadObject("selectMethod", "selectObjectContent2.txt");
    //        uploadObject("selectMethod", "selectObjectContent3.txt");
    //        String sqlExpression = "select * from S3Object";
    //        InputSerialization is = new InputSerialization(null, false, null, null, FileHeaderInfo.USE, null, null, null);
    //        OutputSerialization os = new OutputSerialization(null, null, null, QuoteFields.ASNEEDED, null);
    //        SelectResponseStream stream =
    //                minioClient.selectObjectContent(
    //                        SelectObjectContentArgs.builder()
    //                                .bucket(buckName)
    //                                .object("selectMethod/selectObjectContent3.txt")
    //                                .sqlExpression(sqlExpression)
    //                                .inputSerialization(is)
    //                                .outputSerialization(os)
    //                                .requestProgress(true)
    //                                .build());
    //        byte[] buf = new byte[512];
    //        int bytesRead = stream.read(buf, 0, buf.length);
    //        System.out.println(new String(buf, 0, bytesRead, StandardCharsets.UTF_8));
    //        Stats stats = stream.stats();
    //        System.out.println("bytes scanned: " + stats.bytesScanned());
    //        System.out.println("bytes processed: " + stats.bytesProcessed());
    //        System.out.println("bytes returned: " + stats.bytesReturned());
    //        stream.close();
    //    }
        ///  The operation is not valid for the current state of the object.不知道还需要什么状态
    //    @SneakyThrows
    //    public void restoreObject() {
    //        System.out.println("************************************restoreObject操作***********************************************");
    //        uploadObject("restoreMethod", "restoreObject.txt");
    ////        minioClient.removeObject(RemoveObjectArgs.builder().bucket(buckName).object("restoreMethod/restoreObject.txt").build());
    //        RestoreObjectArgs restoreObjectArgs = RestoreObjectArgs.builder()
    //                .bucket(buckName)
    //                .object("restoreMethod/restoreObject.txt")
    //                .request(new RestoreRequest(null, null, null, null, null, null))
    //                .build();
    //        minioClient.restoreObject(restoreObjectArgs);
    //        System.out.println("***********************************************************************************");
    //    }
        @SneakyThrows
        public void uploadObject(String folder, String fileName) {
            byte[] contentBytes = MinioFileUtils.generateContent(fileName).getBytes(StandardCharsets.UTF_8);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentBytes);
            PutObjectArgs thirdObjectArgs = PutObjectArgs.builder()
                    .bucket(buckName)
                    // 将创建一个名为putMethod文件夹
                    .object(folder + "/" + fileName)
                    // 两者不能同时为-1
                    .stream(byteArrayInputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
                    .build();
            ObjectWriteResponse objectWriteResponse = minioClient.putObject(thirdObjectArgs);
        @SneakyThrows
        private void putObjectByPartSize(String folder, String fileName) {
            String content = MinioFileUtils.generateContentPartSize(fileName, PutObjectArgs.MIN_MULTIPART_SIZE);
            byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentBytes);
            PutObjectArgs thirdObjectArgs = PutObjectArgs.builder()
                    .bucket(buckName)
                    // 将创建一个名为putMethod文件夹
                    .object(folder + "/" + fileName)
                    // 两者不能同时为-1
                    .stream(byteArrayInputStream, -1, PutObjectArgs.MIN_MULTIPART_SIZE)
                    .build();
            minioClient.putObject(thirdObjectArgs);
    
    package com.example;
    import io.minio.*;
    import io.minio.messages.*;
    import com.example.config.MinioProperties;
    import com.example.template.MinioBucketApi;
    import com.example.template.MinioObjectApi;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.boot.WebApplicationType;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.context.ConfigurableApplicationContext;
    import javax.annotation.Resource;
    import java.lang.reflect.Method;
    import java.util.Arrays;
    import java.util.List;
    import java.util.stream.Collectors;
     * Description:
     * @author DawnStar
     * Date: 2023/6/16 19:24
    @SpringBootApplication
    public class MinioApplication implements ApplicationRunner {
        public static void main(String[] args) throws Exception {
            ConfigurableApplicationContext context = new SpringApplicationBuilder(MinioApplication.class).web(WebApplicationType.NONE).run(args);
            MinioBucketApi bean = context.getBean(MinioBucketApi.class);
            invokeMethod(bean);
    //        bean.objectLockConfigurationOperation();
            MinioObjectApi objectApiBean = context.getBean(MinioObjectApi.class);
            objectApiBean.getPresignedPostFormData();
            invokeMethod(objectApiBean);
            System.out.println("结束");
    //        objectApiBean.objectLegalHoldOperation();
    //        SpringApplication.run(MinioApplication.class, args);
        @Resource
        private MinioProperties minioProperties;
        @Override
        public void run(ApplicationArguments args) throws Exception {
            if (minioProperties.getInitBuckets().length == 0) {
                return;
            // 初始化桶
            MinioClient minioClient = MinioClient.builder().credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
                    .endpoint(minioProperties.getEndpoint())
                    .build();
            // 获取所有桶
            List<Bucket> buckets = minioClient.listBuckets();
            List<String> collect = buckets.stream().map(Bucket::name).collect(Collectors.toList());
            List<String> targetBucketNames = Arrays.stream(minioProperties.getInitBuckets()).filter(s -> !collect.contains(s)).collect(Collectors.toList());
            for (String targetBucketName : targetBucketNames) {
                MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(targetBucketName).build();
                minioClient.makeBucket(makeBucketArgs);
            String operationBucket = minioProperties.getOperationBucket();
            boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(operationBucket).build());
            if (!bucketExists) {
                // 只能在创建时候才能设置对象锁,之后都无效
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(operationBucket).objectLock(true).build());
            // 防止bucket被设置了对象锁,无法修改minio服务器的对象
            minioClient.deleteObjectLockConfiguration(DeleteObjectLockConfigurationArgs.builder().bucket(operationBucket).build());
        private static void invokeMethod(Object o) {
            Method[] declaredMethods = o.getClass().getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                if (declaredMethod.getParameters().length > 0) {
                    continue;
                try {
                    if (declaredMethod.getReturnType().equals(void.class) && !declaredMethod.getName().contains("lambda")) {
                        declaredMethod.invoke(o);
                } catch (Exception e) {
                    e.printStackTrace();
                System.out.println("\n\n");
    

    关于Nginx代理问题

    通过nginx代理,https下,通过getPresignedObjectUrl方法获取文件URL可能无法访问指定的文件。 以下是官方给出的一种方案,详情请访问Minio Nginx代理

     location / {
          proxy_set_header Host $http_host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_connect_timeout 300;
          # Default is HTTP/1, keepalive is only enabled in HTTP/1.1
          proxy_http_version 1.1;
          proxy_set_header Connection "";
          chunked_transfer_encoding off;
          proxy_pass https://minio:9000/; # This uses the upstream directive definition to load balance
       location /minio {
          proxy_set_header Host $http_host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header X-NginX-Proxy true;
          # This is necessary to pass the correct IP to be hashed
          real_ip_header X-Real-IP;
          proxy_connect_timeout 300;
          # To support websockets in MinIO versions released after January 2023
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection "upgrade";
          chunked_transfer_encoding off;
          proxy_pass https://minio:9001/; # This uses the upstream directive definition to load balance and assumes a static Console port of 9001
    

    关于秒传,断点续传,分片续传问题

    目前(2023/06/17) Minio Java API没有提供断点续传功能,所以如果想实现上述功能,可以借助Redis,和继承MinioAsyncClient类并将其重写开放部分方法的访问权限来实现相关功能。可以参考这篇博客blog.csdn.net/Alan_ran/ar… 不过他的版本是低的,所以有些方法过时了如 listParts方法便是过时的。

    当然官方API也给予相关的方法是listPartsAsync方法,可以看出listParts方法中也是调用了listPartsAsync方法。更多具体细节请查看S3Base类以及其子类MinioAsyncClient,在8.5.3MinioClinet其实是作为一个工具类,其内部封装了MinioAsyncClient类。

    Minio Java API

    Minio Docker部署

    blog.csdn.net/qq_43437874…

  • 私信