• Object :存储到minio的基本对象,如文件,字节流,Anything...
  • Bucket :用来存储Object的逻辑空间。每个Bucket之间的数据是相互隔离的。对于客户端而言,就相当于一个存放文件的顶层文件夹。
  • Driver :即存储数据的磁盘,在minio启动时,以参数的方式传入。MinIO中所有的对象都会存储在Drive里。
  • Set :即一组Drive的集合,分布式部署根据集群规模自动划分一个或者set,每个set中的drive分布在不同的位置。一个对象存储在一个Set上。
  • 一个对象存储在一个Set上
  • 一个集群划分为多个set
  • 一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算得出
  • 一个Set中的Drive尽可能分布在不同的节点上
  • 2. 纠删码 EC(Erasure Code)

    MinIO使用纠删码机制来保证高可靠性,使用highwayhash 来处理数据损坏 (2 Protection)。纠删码,可以通过数学计算,把丢失的数据进行还原,它可以将n份原始数据,增加m份数据,并能通过n+m份中的任意n份数据,还原为原始数据。即如果有任意小于等于m份的数据失效,仍能通过剩下的数据还原出来。

    3. 单机部署

    中文文档的docker部署有问题,没有暴露控制台的端口。

    单机部署的命令

    基于centos7的部署

    wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio-20230616024106.0.0.x86_64.rpm -O minio.rpm sudo dnf install minio.rpm # 指定 控制台的root用户名和密码 export MINIO_ROOT_USER=admin123 export MINIO_ROOT_PASSWORD=admin123 # 指定 控制台暴露的端口,默认使用动态的端口,并指定数据存放的目录 ./minio server --console-address ":50000" /data/minio-data # 如果上述命令运行成功,可以通过控制台端口50000访问minio后台,证明运行时没问题的,就停掉。然后用下面的命令后台运行minio nohup ./minio server --console-address ":50000" /data/minio-data & -e "MINIO_ROOT_USER=admin123" \ -e "MINIO_ROOT_PASSWORD=admin123" \ -v /home/smy/minio-data/data:/data \ -v /home/smy/minio-data/config:/root/.minio \ minio/minio server /data \ --console-address '0.0.0.0:9999'

    4. SpringBoot整合minio

    遇到的问题

    解决方法:

    修改minio的依赖的okhttp的依赖版本

            <!--minio-->
            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>8.2.1</version>
                <exclusions>
                    <exclusion>
                        <groupId>com.squareup.okhttp3</groupId>
                        <artifactId>okhttp</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.jetbrains.kotlin</groupId>
                        <artifactId>kotlin-stdlib</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
                <version>3.14.9</version>
            </dependency>
            <dependency>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-stdlib</artifactId>
                <version>1.3.70</version>
            </dependency>
    

    https://juejin.cn/post/7209110611858309179

    5. JAVA API 操作 MinIO

    主要包括桶操作和文件操作:

  • 客户端的创建
  • 桶 增删改查
  • 文件 上传 下载 删除(过期删除)
  • minio上传的大小限制,分片上传最小5MB,最大5GB;整个上传最大5TB。并且当不提供objectSize的时候,必须提供partSize(适合上传文件大小未知的场景)。

    putObject 上传文件后的返回对象

    6. 工具类

    package org.example.config;
     * @author myS
     * @description: MinioConfig
     * @date 2023/6/21 17:21
    public class MinioConfig {
        // 端点  单节点
        public static String END_POINT = "http://192.168.1.244:9000";
        // accessKey
        public static String ACCESS_KEY = "admin123";
        // secretKey
        public static String SECRET_KEY = "admin123";
        // bucketName 默认操作的bucket
        public static String BUCKET_NAME = "default";
    

    结合实际业务做拓展

    package org.example.utils;
    import io.minio.*;
    import io.minio.messages.*;
    import org.example.config.MinioConfig;
    import javax.annotation.Nonnull;
    import java.io.InputStream;
    import java.time.ZonedDateTime;
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
     * @author myS
     * @description: MinioUtils
     * @date 2023/6/21 17:20
    public class MinioUtils {
        public static MinioClient minioClient = null;
        static {
            minioClient = MinioClient.builder()
                    .endpoint(MinioConfig.END_POINT)
                    .credentials(MinioConfig.ACCESS_KEY, MinioConfig.SECRET_KEY)
                    .build();
        // 1 桶操作
         * 1 根据 bucketName 判断 bucket 是否存在
         * @param bucketName
         * @return
        public static boolean bucketExists(@Nonnull String bucketName) {
            try {
                return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            } catch (Exception e) {
                e.printStackTrace();
                return false;
         * 2 创建bucket, 使用时不需要提前判断 bucket 是否存在
         * @param bucketName
         * @return
        public static boolean makeBucket(@Nonnull String bucketName) {
            try {
                // 当bucket不存在的时候,创建这个bucket
                if (bucketExists(bucketName)) {
                    minioClient.makeBucket(MakeBucketArgs.builder()
                            .bucket(bucketName)
                            .build());
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
         * 3 根据bucketName删除一个bucket
         * @param bucketName
         * @return
        public static boolean removeBucket(@Nonnull String bucketName) {
            try {
                minioClient.removeBucket(RemoveBucketArgs.builder()
                        .bucket(bucketName)
                        .build());
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
         * 4 获取所有的 bucket, 使用时需要判断返回值是否为null,再进行后续操作
         * @return
        public static List<Bucket> getAllBuckets() {
            try {
                List<Bucket> buckets = minioClient.listBuckets();
                return buckets;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
         * 5 设置 bucket 的声明周期,设置对象过期时间
         * @param bucketName 指定bucket,默认是对指定的这一个桶生效
         * @param expiredDays 过期时间 单位:天
         * @return
        public static boolean setBucketLifecycle (@Nonnull String bucketName, @Nonnull Integer expiredDays) {
            // 创建一个带过期时间规则的配置config
            List<LifecycleRule> rules = new LinkedList<>();
            rules.add(
                    new LifecycleRule(
                            Status.ENABLED,
                            null,
                            new Expiration((ZonedDateTime) null, expiredDays, null),
                            new RuleFilter("search"), // bucket下一级的目录 
                            "expiredRule_1",
                            null,
                            null,
                            null));
            LifecycleConfiguration config = new LifecycleConfiguration(rules);
            // 根据 config的配置,创建一个名称为 my-bucketname 的 bucket
            try {
                minioClient.setBucketLifecycle(
                        SetBucketLifecycleArgs.builder().bucket(bucketName).config(config).build());
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
        // 二 文件操作
         * 上传文件并返回是否上传成功
         * @param bucketName
         * @param inputStream 待上传文件的输入流
         * @return
        public static boolean upload (@Nonnull InputStream inputStream, @Nonnull String bucketName, @Nonnull String objectName) {
            try {
                ObjectWriteResponse objectWriteResponse = minioClient.putObject(
                        PutObjectArgs.builder()
                                .bucket(bucketName)
                                .stream(inputStream, -1, 1024 * 1024 * 10L) // 分片上传 10M
                                .object(objectName)
                                .build());
                System.out.println(objectWriteResponse.region());
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
         * 根据bucket和objectName返回一个minio中的对象的输入流
         * 因为可能为null,所以使用前需要先判null,避免发生其他错误
         * @param bucketName
         * @param objectName
         * @return
        public static InputStream download (@Nonnull String bucketName, @Nonnull String objectName) {
            try {
                return minioClient.getObject(GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());
            } catch (Exception e) {
                e.printStackTrace();
                return null;
         * 删除指定bucket下的名为objectName的对象
         * @param bucketName
         * @param objectName
         * @return
        public static boolean removeObject(@Nonnull String bucketName, @Nonnull String objectName) {
            try {
                minioClient.removeObject(RemoveObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                        .build());
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
         * 获取 当前bucket下的所有对象(只会列出一层)
         * @param bucketName
         * @return
        public static List<String> getAllObjects (@Nonnull String bucketName) {
            List<String> allObjects = new ArrayList<>();
                Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .build());
                results.forEach(item -> {
                    Item item1 = null;
                    try {
                        item1 = item.get();
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw new RuntimeException(e);
                    allObjects.add(item1.objectName());
            } catch (Exception e) {
                e.printStackTrace();
            return allObjects;
    

    工具类测试类

    package org.example;
    import io.minio.MinioClient;
    import io.minio.messages.Bucket;
    import org.example.utils.MinioUtils;
    import org.junit.Assert;
    import org.junit.Test;
    import java.io.*;
    import java.nio.file.Files;
    import java.util.List;
    import java.util.Objects;
    import java.util.stream.Stream;
     * @author myS
     * @description: TODO
     * @date 2023/6/21 21:47
    public class MinioTest {
    //    public static MinioClient minioClient = MinioUtils.minioClient;
         * 测试 getAllBuckets 只能列出最外层的bucket,里层嵌套的bucket不能列出
         * logs
         * my-bucketname
         * test
        @Test
        public void testList () {
            List<Bucket> test = MinioUtils.getAllBuckets();
            test.forEach(item -> {
                System.out.println(item.name());
         * 因为 inputStream 无法获取到 源文件的名字,所以需要指定一个 objectName,
         * 这个参数如果以 / 开头,就会新建一层目录;
         * 如果不以 / 开头,就会直接在 bucketName 下上传文件
         * 如果文件名和目录名 重复,后面的会覆盖前面的,前面的会在删除后面的之后显示出来  ****
         * @throws FileNotFoundException
        @Test
        public void testUpload () throws FileNotFoundException {
            File file = new File("C:\\Users\\10170\\Desktop\\desktop\\cnblog\\待整理.md");
            InputStream inputStream = new FileInputStream(file);
            // 上传的文件路径: test/hdhd
            boolean upload = MinioUtils.upload(inputStream,"test","/hdhd");
            System.out.println(upload);
            // 上传的文件路径: test/hdhd/待整理.md
            boolean upload1 = MinioUtils.upload(inputStream,"test","/hdhd/"+file.getName());
            System.out.println(upload1);
         * 测试 minio 的下载函数
         * 函数返回一个输入流,表示从文件写入内存中,用完close释放
         * 服务器程序拿到这个输入流之后,可以通过网络返回给前端
         * @throws IOException
        @Test
        public void testDownload () throws IOException {
            InputStream stream = MinioUtils.download("logs", "b50aa3fcb9fa4f64a4e295b7a996aa9a/events.json");
            // stream 判 null
            if (Objects.isNull(stream)) {
                System.out.println("文件流为null!!!!! 下载失败");
                return ;
            // 读取流 并下载
            File targetFile = new File("events.json");
            FileOutputStream fileOutputStream = new FileOutputStream(targetFile);
            byte[] bytes = new byte[1024];
            int length;
            while ((length = stream.read(bytes)) > 0) {
                fileOutputStream.write(bytes, 0, length);
            fileOutputStream.close();
            stream.close();
         * 删除 minio 文件对象测试
        @Test
        public void testRemove() {
            boolean result = MinioUtils.removeObject("test", "hdhd/待整理.md");
            Assert.assertTrue(result);
        @Test
        public void getAllObjects(){
            List<String> test = MinioUtils.getAllObjects("logs");
            test.forEach(System.out::println);