最新要闻

广告

手机

光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯

光庭信息跌4.57% 2021上市超募11亿2022扣非降74% 时快讯

搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注

搜狐汽车全球快讯 | 大众汽车最新专利曝光:仪表支持拆卸 可用手机、平板替代-环球关注

家电

全球观热点:MinIO-对象存储简单使用

来源:博客园

MinIO

1. 基础概念

  • 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.rpmsudo dnf install minio.rpm# 指定 控制台的root用户名和密码export MINIO_ROOT_USER=admin123export MINIO_ROOT_PASSWORD=admin123# 指定 控制台暴露的端口,默认使用动态的端口,并指定数据存放的目录./minio server --console-address ":50000" /data/minio-data# 如果上述命令运行成功,可以通过控制台端口50000访问minio后台,证明运行时没问题的,就停掉。然后用下面的命令后台运行minionohup  ./minio server --console-address ":50000" /data/minio-data &

控制台上传一个文件可以在服务器对应的目录中找到文件,服务正常运行。

基于docker的部署

docker 部署命令

docker pull docker://minio/minio### 新版docker run --name minio \-p 9000:9000 \-p 9999:9999 \-d --restart=always \-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的依赖版本

                    io.minio            minio            8.2.1                                                com.squareup.okhttp3                    okhttp                                                    org.jetbrains.kotlin                    kotlin-stdlib                                                        com.squareup.okhttp3            okhttp            3.14.9                            org.jetbrains.kotlin            kotlin-stdlib            1.3.70        

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 getAllBuckets() {        try {            List 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 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 getAllObjects (@Nonnull String bucketName) {        List allObjects = new ArrayList<>();        try{            Iterable> 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 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 test = MinioUtils.getAllObjects("logs");        test.forEach(System.out::println);    }}

关键词: