MinIO快速入门(什么是MinIO、MinIO中的基本概念、在Java代码中操作MinIO、将操作MinIO的Java代码封装成一个starter、控制匿名用户对桶的访问权限)

9 篇文章 0 订阅
1 篇文章 0 订阅

1. 前置知识

1.1 文件的存储方式

在开发一个 Web 应用时,我们有多种方式存储图片、视频、办公文档等文件,以下是几种主要的存储方式

存储方式优点缺点
服务器磁盘开发便捷,成本低扩展困难
分布式文件系统实现扩容非常方便复杂度高
第三方存储开发简单,功能强大,免维护收费
  • 存储在服务器的磁盘上:实现起来非常简单,但是如果文件越来越多,或者在特定的业务场景下单个文件的内容就很大,就算你的硬盘有 2TB 的空间,服务器的磁盘空间迟早会被占满,而且将文件存储在服务器的硬盘,后期扩展将变得十分麻烦

  • 存储在分布式文件系统中:实现扩容非常方便,但相对于直接存储在服务器硬盘上来说,实现也更加复杂

  • 第三方存储:国内比较优秀的第三方存储服务有阿里云的对象存储服务(对象存储 OSS_云存储服务_企业数据管理_存储-阿里云 (aliyun.com)),缺点当然就是收费

OSS,Object Storage Service,对象存储服务

1.2 主流的分布式文件系统

存储方式优点缺点
FastDFS(国产开源)1. 主备服务,高可用
2.支持主从文件,支持自定义扩展名
3.支持动态扩容
1. 没有完备官方文档,近几年没有更新
2. 环境搭建较为麻烦
MinIO1. 性能高,标准硬件条件下 MinIO 能达到55GB/s的读、35GB/s的写速率
2. 部署自带管理界面
3. MinI0.Inc运营的开源项目,社区活跃度高
4. 提供了所有主流开发语言的SDK
不支持动态增加节点

对比下来,MinIO 更适合在项目开发中使用

2. 什么是MinIO

MinlO 是基于 Apache License v2.0 开源协议的的对象存储服务,可以做为云存储的解决方案,保存海量的图片,视频文档


MinIO 的优点:

  1. GoLang 语言实现,配置简单,单行命令可以运行起来
  2. MinIO 兼容亚马逊 S3 云存储服务接口,适合于存储大容量非结构化的数据,一个对象文件可以是任意大小,从几 KB 到最大的 5TB

MinIO 的官网文档:MinIO对象存储 Kubernetes(提供了中文开发文档)

3. 安装MinIO

我们通过 docker 来安装 MinIO

3.1 搜索MinIO镜像

sudo docker search minio

在这里插入图片描述

我们选择 STARS 数最高的

3.2 下载MinIO镜像

通过以下指令下载 MinIO 镜像

sudo docker pull minio/minio

3.3 将MinIO镜像保存到本地(可跳过)

下载好 ElasticSearch 的镜像后,可以将镜像保存为 tar 文件,下载到 Windows 本地,方便下一次在另一个 Linux 系统上运行

sudo docker save minio/minio:latest -o /tmp/minio-latest.tar
sudo chmod +rx /tmp/minio-latest.tar

保存的 tar 文件,命名中不要有英文的冒号:,因为 Windows 环境下文件的命名是不允许出现英文冒号的,如果文件名中有英文冒号,Windows 系统会自动截掉英文冒号以及英文冒号后面的部分

3.4 创建MinIO容器并启动MinIO

  • 旧版的 MinIO 可能控制台端口和访问端口都是同一个
  • 最新版本的 MinIO 已经将控制台端口和访问端口分离

运行以下指令创建 MinIO 容器并启动 MinIO

sudo docker run \
    -p 9000:9000 \
    -p 9090:9090 \
    --name minio \
    --restart=always \
    -e "MINIO_ACCESS_KEY=wuyanzu" \
    -e "MINIO_SECRET_KEY=bo@DwF1mzr_wF7am" \
    -v /home/data:/data \
    -v /home/config:/root/.minio \
    -d \
    minio/minio server /data --address ":9000" --console-address ":9090"

指令的详细解释:

  • sudo docker run: 这是 Docker 命令,用于启动一个新的容器实例
  • -p 9000:9000: 这个参数将宿主机的端口 9000 映射到容器内的端口 9000。MinIO 服务通常使用这个端口来处理 API 请求
  • -p 9090:9090: 这个参数将宿主机的端口 9090 映射到容器内的端口 9090。MinIO 控制台(Web 界面)通常使用这个端口。
  • --name minio: 这个参数为容器指定了一个名称,这里命名为 “minio”
  • --restart=always: 这个参数指定了容器的重启策略。如果容器退出,Docker 将始终尝试重启它
  • -e "MINIO_ACCESS_KEY=wuyanzu": 这个参数设置了一个环境变量,MINIO_ACCESS_KEY 是 MinIO 服务的访问密钥,用于身份验证。这里的值被设置为 “wuyanzu”
  • -e "MINIO_SECRET_KEY=bo@DwF1mzr_wF7am": 这个参数设置了一个环境变量,MINIO_SECRET_KEY 是 MinIO 服务的秘密密钥,也用于身份验证。这里的值是一个复杂的密码
  • -v /home/data:/data: 这个参数将宿主机的 /home/data 目录挂载到容器内的 /data 目录。MinIO 将在这个目录中存储数据
  • -v /home/config:/root/.minio: 这个参数将宿主机的 /home/config 目录挂载到容器内的 /root/.minio 目录。MinIO 将在这个目录中存储配置文件
  • -d: 这个参数指示 Docker 以守护进程模式运行容器,即容器将在后台运行
  • minio/minio: 这是使用的 Docker 镜像名称,这里使用的是 MinIO 官方镜像
  • server /data: 这是传递给 MinIO 镜像的命令和参数。server 是 MinIO 的命令,用于启动 MinIO 服务,/data 是 MinIO 存储数据的目录(/data 是容器内的目录)
  • --address ":9000": 这个参数指定了 MinIO 服务器监听的地址和端口,这里指定的是容器内的 9000 端口
  • --console-address ":9090": 这个参数指定了 MinIO 控制台(Web 界面)监听的地址和端口,这里指定的是容器内的 9090 端口

3.5 开放防火墙的端口

为了能够从外界访问 MinIO,需要为 MinIO 开放防火墙的 9000 端口 和 9090 端口

  1. 如果你使用的是云服务器,在安全组中放行 9000 端口 和 9090 端口
  2. 如果你安装了宝塔,除了在安全组中放行 9000 端口 和 9090 端口,还要在宝塔中放行 9000 端口 和 9090 端口

完成以上两个操作后,输入以下指令开放 9000 端口 和 9090 端口

Ubuntu

sudo ufw allow 9000 
sudo ufw allow 9090 
sudo ufw reload

CentOS

sudo firewall-cmd --zone=public --add-port=9000 /tcp --permanent
sudo firewall-cmd --zone=public --add-port=9090 /tcp --permanent
sudo firewall-cmd --reload

3.6 访问MinIO控制台

在浏览器输入以下内容,访问 MinIO 的控制台(注意将 IP 地址改为你的 IP 地址)

http://127.0.0.1:9090

登录页如下

在这里插入图片描述

输入用户名和密码后进入 MinIO 的控制台(用户名和密码在启动 MinIO 的指令中)


登录成功后的页面

在这里插入图片描述

4. MinIO中的基本概念

4.1 Bucket(桶)

MinIO 中的 Bucket 可以理解为 Windows 中的盘符( 如 C 盘、D 盘)


我们在 MinIO 的控制台中新建一个桶

在这里插入图片描述

在这里插入图片描述

4.2 Path / Prefix(路径)

MinIO 中的 Bucket 可以理解为 Windows 中的文件夹


我们在刚创建的桶中新建一个 Path (Path 的名称中最好不要有中文

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.3 文件

MinIO 中的文件跟 Windows 系统中的文件概念相似

4.3.1 在控制台中上传文件

我们新建一个名为 index.html 的 HTML 文件,并将这个 HTML 文件上传到 MinIO 中

HTML 文件的内容如下

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello, MinIO</title>
    <style>
        body {
            background-color: black;
            font-family: 'Arial', sans-serif;
            text-align: center;
            color: white;
            margin-top: 100px;
        }

        h1 {
            font-size: 48px;
            margin: 0;
        }
    </style>
</head>
<body>
<h1>Hello, MinIO</h1>
</body>
</html>

可以上传文件,也可以上传文件夹,我们选择上传文件

在这里插入图片描述

上传成功后的页面

在这里插入图片描述

4.3.2 在控制台中下载文件

点击文件名,再点击 Download

在这里插入图片描述

4.3.3 在控制台中分享文件

我们也可以将文件分享给我们的小伙伴

点击 Share 后会生成一个链接,在浏览器中访问这个链接就能够下载这个文件

在这里插入图片描述

5. MinIO快速入门(在 Java 代码中操作 MinIO)

5.1 引入依赖

要在 Java 代码中操作 MinIO,需要先提供 MinIO 的 Maven 依赖

<!-- https://mvnrepository.com/artifact/io.minio/minio -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.10</version>
</dependency>

同时为了更方便地记录日志,我们引入 lombok 依赖(主要是为了使用 @Slf4j 注解)

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <scope>provided</scope>
</dependency>

5.2 编写测试类,获取MinIO中的文件

注意:在 Java 代码中连接 MinIO 使用的是 9000 端口,9090 端口是 MinIO 控制台的访问地址,而 9000 才是 MinIO 对外暴露的端口

import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.MinioClient;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

@Slf4j
public class MinioTests {

    private MinioClient minioClient;

    @Test
    public void testGetObject() throws Exception {
        String path = "niekeyi/";

        GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                .bucket("blog")
                .object(path + "index.html")
                .build();

        GetObjectResponse getObjectResponse = minioClient.getObject(getObjectArgs);
        getObjectResponse.transferTo(System.out);
    }

    @BeforeEach
    public void init() {
        minioClient = MinioClient.builder().
                credentials("wuyanzu", "bo@DwF1mzr_wF7am").
                endpoint("http://127.0.0.1:9000").
                build();
    }

    @AfterEach
    public void shutdown() throws Exception {
        // 关闭连接,释放资源。
        minioClient.close();
    }

}

在控制台中就可以获取到刚上传的 HTML 文件

在这里插入图片描述

5.3 上传文件到MinIO

import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

@Slf4j
public class MinioTests {

    private MinioClient minioClient;

    @Test
    public void testGetObject() throws Exception {
        String path = "niekeyi/";

        GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                .bucket("blog")
                .object(path + "index.html")
                .build();

        GetObjectResponse getObjectResponse = minioClient.getObject(getObjectArgs);
        getObjectResponse.transferTo(System.out);
    }

    @Test
    public void testPutObject() {
        InputStream inputStream;
        String filePath = "F://MinIO/一个真正的MAN.mp4";
        try {
            inputStream = new FileInputStream(filePath);
        } catch (IOException e) {
            log.error("文件不存在:{}", e.getMessage());
            return;
        }

        try {
            PutObjectArgs putObjectArgs;
            String bucketName = "blog";
            String path = "niekeyi/";
            String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
            putObjectArgs = PutObjectArgs.builder()
                    .contentType("video/mp4") // 文件类型
                    .bucket(bucketName) // 桶名称
                    .object(path + fileName) // 文件名
                    .stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);
            System.err.println("http://127.0.0.1:9000/" + bucketName + "/" + fileName);
        } catch (Exception e) {
            log.error("上传失败:{}", e.getMessage());
        }
    }

    @BeforeEach
    public void init() {
        minioClient = MinioClient.builder().
                credentials("wuyanzu", "bo@DwF1mzr_wF7am").
                endpoint("http://127.0.0.1:9000").
                build();
    }

    @AfterEach
    public void shutdown() throws Exception {
        // 关闭连接,释放资源。
        minioClient.close();
    }

}

打开 MinIO 的控制台,发现文件已经成功上传了

在这里插入图片描述

6. 将操作MinIO的Java代码封装成一个starter

如果你想让更多的人能够使用你的 starter,可以使用较低版本的 JDK 和较低版本的 SpringBoot 开发

由于 MinIO 官方并没有提供操作 MinIO 的 starter,而 MinIO 的主要功能是存储文件,我们在其它项目中也有很大的概率会用到 MinIO,所以我们可以将操作 MinIO 的 Java 代码封装成一个 starter,以后需要通过 Java 代码操作 MinIO 时,直接引入 starter,做少量的配置就能使用了

不知道如何自定义 starter 的,可以参考我的另一篇博文:SpringBoot自定义starter(starter的命名规范、starter的结构、自定义starter、为配置属性添加描述信息、检验配置属性

当然,市面上也有一些第三方的基于 MinioClient 的 starter ,可以在 GitHub 或 Gitee 上搜索

6.1 创建父工程

在这里插入图片描述

在这里插入图片描述

创建工程后先修改 Maven 仓库路径和 Maven 的配置文件

在这里插入图片描述

6.2 初始化父工程

父工程不需要 src 目录,我们删除父工程的 src 目录

在这里插入图片描述

接着在父工程的 pom.xml 文件的 properties 标签中添加以下内容,表明父工程是一个聚合工程

在这里插入图片描述

<packaging>pom</packaging>

引入 MinIO 的 Maven 依赖

<!-- https://mvnrepository.com/artifact/io.minio/minio -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.10</version>
</dependency>

最后把 <build></build> 标签也删掉,因为父工程并不需要打包插件

6.3 创建 autoconfigure 模块

右键父工程

在这里插入图片描述

选择普通的 Maven 项目

minio-spring-boot-starter

在这里插入图片描述

6.4 创建 starter 模块

在这里插入图片描述

6.5 在 starter 模块中引入 autoconfigure 模块的依赖

在 starter 模块的 pom.xml 文件中引入 autoconfigure 模块的依赖

<dependencies>
    <dependency>
        <groupId>cn.edu.scau</groupId>
        <artifactId>minio-spring-boot-autoconfigure</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>

    <!--如果当前starter需要依赖其它的starter或jar包,在这里导入-->
</dependencies>

6.6 新建配置类

在 autoconfigure 模块下新建一个配置类,配置类命名为 MinioAutoConfiguration

package cn.edu.scau;

import org.springframework.context.annotation.Configuration;

@Configuration
public class MinioAutoConfiguration {

}

6.7 新建配置属性类

在 autoconfigure 模块下新建一个 MinioConfigurationProperties 配置属性类

package cn.edu.scau;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "minio")
public class MinioConfigurationProperties {

}

prefix = “minio” 指的是在配置文件中填写配置属性时的前缀

6.8 让用户在配置文件中填写配置属性时有提示

为了在 application.yml 文件或 application.properties 文件中填写配置属性时有提示,我们需要完成两个步骤

第一步:在 autoconfigure 模块中添加文件处理器的依赖

<!--导入配置文件处理器,配置文件进行绑定就会有提示-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

第二步:在 autoconfigure 模块的 META-INF/spring 目录下创建 org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件

org.springframework.boot.autoconfigure.AutoConfiguration.imports

文件内容如下

cn.edu.scau.MinioAutoConfiguration

完成上述两个步骤后,MinioConfigurationProperties 类的 @ConfigurationProperties 可能会给出警告信息,因为 MinioConfigurationProperties 是一个配置属性类,但它并没有没有与任何一个由 Spring 管理的配置类绑定在一起

在这里插入图片描述

我们只需要将 MinioAutoConfiguration 类与 ConfigurationProperties 类绑定在一起就可以了,可以通过 EnableConfigurationProperties 注解绑定

@EnableConfigurationProperties(MinioConfigurationProperties.class)

在这里插入图片描述

6.9 编写 MinioConfigurationProperties 类

MinioConfigurationProperties 类主要关心连接 MinIO 所需要的几个参数

  • endpoint

  • accessKey

  • secretKey

  • bucket

package cn.edu.scau;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "minio")
public class MinioConfigurationProperties {

    private String endpoint;

    private String accessKey;

    private String secretKey;

    private String bucket;

    public String getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    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 getBucket() {
        return bucket;
    }

    public void setBucket(String bucket) {
        this.bucket = bucket;
    }

    @Override
    public String toString() {
        return "MinioConfigurationProperties{" +
                "endpoint='" + endpoint + '\'' +
                ", accessKey='" + accessKey + '\'' +
                ", secretKey='" + secretKey + '\'' +
                ", bucket='" + bucket + '\'' +
                '}';
    }

}

6.10 将 MinioClient 对象注入容器中

package cn.edu.scau;

import io.minio.MinioClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(MinioConfigurationProperties.class)
public class MinioAutoConfiguration {

    private final MinioConfigurationProperties minioConfigurationProperties;

    public MinioAutoConfiguration(MinioConfigurationProperties minioConfigurationProperties) {
        this.minioConfigurationProperties = minioConfigurationProperties;
    }

    @Bean
    public MinioClient minioClient() {
        return MinioClient
                .builder()
                .credentials(minioConfigurationProperties.getAccessKey(), minioConfigurationProperties.getSecretKey())
                .endpoint(minioConfigurationProperties.getEndpoint())
                .build();
    }

}

6.11 创建 MinioFileStorageUtil 类

MinioFileStorageUtil 类是 starter 的核心类,封装了大量的操作 MinIO 的代码

package cn.edu.scau;


import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
@EnableConfigurationProperties(MinioConfigurationProperties.class)
@Import(MinioAutoConfiguration.class)
public class MinioFileStorageUtil {

    private final MinioClient minioClient;

    private final MinioConfigurationProperties minioConfigurationProperties;

    public MinioFileStorageUtil(MinioClient minioClient, MinioConfigurationProperties minioConfigurationProperties) {
        this.minioClient = minioClient;
        this.minioConfigurationProperties = minioConfigurationProperties;
    }

    private final static String separator = "/";

    /**
     * @param path     文件路径
     * @param filename yyyy/MM/dd/filename.jpg
     * @return String
     */
    public String buildFilePath(String path, String filename) {
        StringBuilder stringBuilder = new StringBuilder(50);
        if (path != null && !path.isEmpty()) {
            stringBuilder.append(path).append(separator);
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");
        String todayString = simpleDateFormat.format(new Date());
        stringBuilder.append(todayString)
                .append(separator)
                .append(filename);
        return stringBuilder.toString();
    }

    /**
     * 上传图片文件-jpg
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */
    public String uploadImageFile(String prefix, String filename, InputStream inputStream) {
        String filePath = buildFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .contentType("image/jpg")
                    .bucket(minioConfigurationProperties.getBucket()).stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);

            return minioConfigurationProperties.getEndpoint() + separator + minioConfigurationProperties.getBucket() + separator + filePath;
        } catch (Exception e) {
            log.error("Minio put file error.", e);
            throw new RuntimeException("Minio put file error.");
        }
    }

    /**
     * 上传html文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */
    public String uploadHtmlFile(String prefix, String filename, InputStream inputStream) {
        String filePath = buildFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .contentType("text/html")
                    .bucket(minioConfigurationProperties.getBucket()).stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);
            return minioConfigurationProperties.getEndpoint() + separator + minioConfigurationProperties.getBucket() + separator + filePath;
        } catch (Exception e) {
            log.error("Minio put file error.", e);
            throw new RuntimeException("Minio put file error.");
        }
    }

    /**
     * 上传mp4文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */
    public String uploadMp4File(String prefix, String filename, InputStream inputStream) {
        String filePath = buildFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .contentType("video/mp4")
                    .bucket(minioConfigurationProperties.getBucket()).stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);
            return minioConfigurationProperties.getEndpoint() + separator + minioConfigurationProperties.getBucket() + separator + filePath;
        } catch (Exception e) {
            log.error("Minio put file error.", e);
            throw new RuntimeException("Minio put file error.");
        }
    }

    /**
     * 上传文件,由 MinIO 自动推断文件类型
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */
    public String uploadFile(String prefix, String filename, InputStream inputStream) {
        String filePath = buildFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath)
                    .bucket(minioConfigurationProperties.getBucket()).stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);
            return minioConfigurationProperties.getEndpoint() + separator + minioConfigurationProperties.getBucket() + separator + filePath;
        } catch (Exception e) {
            log.error("Minio put file error.", e);
            throw new RuntimeException("Minio put file error.");
        }
    }

    /**
     * 删除文件
     *
     * @param pathUrl 文件全路径
     */
    public void deleteFile(String pathUrl) {
        String key = pathUrl.replace(minioConfigurationProperties.getEndpoint() + separator, "");
        int index = key.indexOf(separator);
        String bucket = key.substring(0, index);
        String filePath = key.substring(index + 1);

        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                .bucket(bucket)
                .object(filePath)
                .build();

        try {
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("minio remove file error.  pathUrl:{}", pathUrl);
            throw new RuntimeException("Minio remove file error.  pathUrl:" + pathUrl);
        }
    }


    /**
     * 下载文件
     *
     * @param pathUrl 文件全路径
     * @return 字节数组
     */
    public byte[] downloadFile(String pathUrl) {
        String key = pathUrl.replace(minioConfigurationProperties.getEndpoint() + separator, "");
        int index = key.indexOf(separator);
        String filePath = key.substring(index + 1);
        InputStream inputStream;

        try {
            GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                    .bucket(minioConfigurationProperties.getBucket())
                    .object(filePath)
                    .build();
            inputStream = minioClient.getObject(getObjectArgs);
        } catch (Exception e) {
            log.error("Minio down file error.  pathUrl:{}", pathUrl);
            throw new RuntimeException("Minio down file error.  pathUrl:" + pathUrl);
        }

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int returnCode = 0;
        while (true) {
            try {
                if (inputStream != null && !((returnCode = inputStream.read(buffer, 0, 1024)) > 0)) {
                    break;
                }
            } catch (IOException e) {
                throw new RuntimeException("Minio transfer file to byte array failed.");
            }
            byteArrayOutputStream.write(buffer, 0, returnCode);
        }

        return byteArrayOutputStream.toByteArray();
    }

}

6.12 将 MinioFileStorageUtil 类注入容器中

package cn.edu.scau;

import io.minio.MinioClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(MinioConfigurationProperties.class)
public class MinioAutoConfiguration {

    private final MinioConfigurationProperties minioConfigurationProperties;

    public MinioAutoConfiguration(MinioConfigurationProperties minioConfigurationProperties) {
        this.minioConfigurationProperties = minioConfigurationProperties;
    }

    @Bean
    public MinioClient minioClient() {
        return MinioClient
                .builder()
                .credentials(minioConfigurationProperties.getAccessKey(), minioConfigurationProperties.getSecretKey())
                .endpoint(minioConfigurationProperties.getEndpoint())
                .build();
    }
    
    @Bean
    public MinioFileStorageUtil minioFileStorageService(MinioClient minioClient, MinioConfigurationProperties minioConfigurationProperties) {
        return new MinioFileStorageUtil(minioClient, minioConfigurationProperties);
    }

}

6.13 打包

在父工程中打包

在这里插入图片描述

7. 测试封装好的 starter

新建一个 SpringBoot 工程,测试封装好的 starter

7.1 引入依赖

在测试工程中引入封装好的 starter

<dependency>
    <groupId>cn.edu.scau</groupId>
    <artifactId>minio-spring-boot-starter</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

starter 的名字要与你在 starter 模块中对应的 artifactId 对应

在这里插入图片描述

7.2 编写配置文件

编写测试工程的配置文件(application.yml)

可以看到,编写配置文件时有提示了

在这里插入图片描述

minio:
  endpoint: http://127.0.0.1:9000
  accessKey: wuyanzu
  secretKey: bo@DwF1mzr_wF7am
  bucket: blog

7.3 编写测试类

7.3.1 测试下载文件

package cn.edu.scau;

import cn.edu.scau.MinioConfigurationProperties;
import cn.edu.scau.MinioFileStorageUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

@Slf4j
@SpringBootTest
public class MinioStarterTests {

    @Autowired
    private MinioFileStorageUtil minioFileStorageUtil;

    @Autowired
    private MinioConfigurationProperties minioConfigurationProperties;

    @Test
    public void testDownLoadFile() throws FileNotFoundException {
        String endpoint = minioConfigurationProperties.getEndpoint();
        String bucket = minioConfigurationProperties.getBucket();

        String transferToPath = "F://MinIO/index.html";
        byte[] bytes = minioFileStorageUtil.downloadFile(endpoint + "/" + bucket + "/" + "niekeyi/index.html");
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(transferToPath));
        int bufferSize = 1024 * 1024; // 1MB
        try {
            int offset = 0;
            while (offset < bytes.length) {
                int len = bytes.length - offset;
                if (len > bufferSize) {
                    len = bufferSize;
                }
                bufferedOutputStream.write(bytes, offset, len);
                offset += len;
            }

            bufferedOutputStream.flush();
            bufferedOutputStream.close();
            log.info("文件转存成功:{}", transferToPath);
        } catch (Exception e) {
            log.error("文件转存失败:{}", e.getMessage());
            throw new RuntimeException("文件转存失败");
        }
    }
    
}

7.3.2 测试上传文件

上传文件后会返回访问文件的 URL

package cn.edu.scau;

import cn.edu.scau.MinioConfigurationProperties;
import cn.edu.scau.MinioFileStorageUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

@Slf4j
@SpringBootTest
public class MinioStarterTests {

    @Autowired
    private MinioFileStorageUtil minioFileStorageUtil;

    @Autowired
    private MinioConfigurationProperties minioConfigurationProperties;

    @Test
    public void testDownLoadFile() throws FileNotFoundException {
        String endpoint = minioConfigurationProperties.getEndpoint();
        String bucket = minioConfigurationProperties.getBucket();

        String transferToPath = "F://MinIO/index.html";
        byte[] bytes = minioFileStorageUtil.downloadFile(endpoint + "/" + bucket + "/" + "niekeyi/index.html");
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(transferToPath));
        int bufferSize = 1024 * 1024; // 1MB
        try {
            int offset = 0;
            while (offset < bytes.length) {
                int len = bytes.length - offset;
                if (len > bufferSize) {
                    len = bufferSize;
                }
                bufferedOutputStream.write(bytes, offset, len);
                offset += len;
            }

            bufferedOutputStream.flush();
            bufferedOutputStream.close();
            log.info("文件转存成功:{}", transferToPath);
        } catch (Exception e) {
            log.error("文件转存失败:{}", e.getMessage());
            throw new RuntimeException("文件转存失败");
        }
    }

    @Test
    public void uploadHtmlFile() throws FileNotFoundException {
        FileInputStream fileInputStream = new FileInputStream("F://MinIO/index.html");
        String filePath = minioFileStorageUtil.uploadHtmlFile("niekeyi", "index.html", fileInputStream);
        log.info("文件上传成功:{}", filePath);
    }

    @Test
    public void uploadMp4File() throws FileNotFoundException {
        FileInputStream fileInputStream = new FileInputStream("F://MinIO/一个真正的MAN.mp4");
        String filePath = minioFileStorageUtil.uploadMp4File("niekeyi", "一个真正的MAN.mp4", fileInputStream);
        log.info("文件上传成功:{}", filePath);
    }

    @Test
    public void uploadFile() throws FileNotFoundException {
        FileInputStream fileInputStream = new FileInputStream("F://MinIO/2024年9月5日.txt");
        String filePath = minioFileStorageUtil.uploadFile("niekeyi", "2024年9月5日.txt", fileInputStream);
        log.info("文件上传成功:{}", filePath);
    }

}

8. 控制匿名用户对桶对的访问权限

利用 MinioClient 上传文件到 MinIO 后,利用返回的 URL 直接在浏览器访问,会出现以下错误

在这里插入图片描述

This XML file does not appear to have any style information associated with it. The document tree is shown below.

AccessDenied

Access Denied.

niekeyi/2024/09/05/一个真正的MAN.mp4

blog

/blog/niekeyi/2024/09/05/一个真正的MAN.mp4

17F267748F33E80C

dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8


因为在浏览器中访问 MinIO 是以匿名用户的身份访问的,MinIO 的桶默认不对匿名用户开放

解决方法:为匿名用户开启桶的访问权限

在这里插入图片描述

在这里插入图片描述

  • / 表示可以访问桶中的任意路径
  • read only 表示只读

在这里插入图片描述

9. 源代码

自定义 minio-sprint-boot-starter 的源代码已经放到了 Gitee 上:minio-starter

要在Java代码使用MinIO SDK上传文件,您需要完以下步骤: 1. 添加MinIO SDK的依赖项。您可以使用Maven或Gradle将其添加到您的项目,例如: 使用Maven: ```xml <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.1.0</version> </dependency> ``` 使用Gradle: ``` implementation 'io.minio:minio:8.1.0' ``` 2. 在代码创建MinioClient对象并初始化,例如: ```java import io.minio.*; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; public class MinioUploader { private static final String ENDPOINT = "http://localhost:9000"; private static final String ACCESS_KEY = "ACCESS_KEY"; private static final String SECRET_KEY = "SECRET_KEY"; public static void main(String[] args) throws NoSuchAlgorithmException, IOException, InvalidKeyException { MinioClient minioClient = MinioClient.builder() .endpoint(ENDPOINT) .credentials(ACCESS_KEY, SECRET_KEY) .build(); // your upload code here } } ``` 请注意,您需要将`ENDPOINT`,`ACCESS_KEY`和`SECRET_KEY`更改为您的MinIO服务器的正确值。 3. 上传文件。您可以使用`putObject`方法上传文件,例如: ```java import io.minio.*; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; public class MinioUploader { private static final String ENDPOINT = "http://localhost:9000"; private static final String ACCESS_KEY = "ACCESS_KEY"; private static final String SECRET_KEY = "SECRET_KEY"; public static void main(String[] args) throws NoSuchAlgorithmException, IOException, InvalidKeyException { MinioClient minioClient = MinioClient.builder() .endpoint(ENDPOINT) .credentials(ACCESS_KEY, SECRET_KEY) .build(); String bucketName = "my-bucket"; String objectName = "my-object"; String fileName = "/path/to/file"; PutObjectArgs putObjectArgs = PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .filename(fileName) .build(); minioClient.putObject(putObjectArgs); System.out.println("File uploaded successfully"); } } ``` 请注意,您需要将`bucketName`,`objectName`和`fileName`更改为您自己的值。 这就是上传文件到MinIO的基本步骤。您可以根据需要调整代码和参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

聂 可 以

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值