SpringBoot中集成Minio高性能分布式存储文件服务入门

场景

若依前后端分离版手把手教你本地搭建环境并运行项目:

若依前后端分离版手把手教你本地搭建环境并运行项目-CSDN博客

参考上面搭建项目。

Minio

Minio是基于Go语言编写的对象存储服务,适合于存储大容量非结构化的数据,例如图片、音频、视频、日志文件、备份数据和容器/虚拟机镜像等,

而一个对象文件可以是任意大小,从几kb到最大5T不等。区别于分布式存储系统,minio的特色在于简单、轻量级,对开发者友好。

特点

简单、可靠:

Minio采用简单可靠的集群方案,摒弃复杂的大规模的集群调度管理,减少风险与性能瓶颈,聚焦产品的核心功能,

打造高可用的集群、灵活的扩展能力以及超过的性能。建立众多的中小规模、易管理的集群,

支持跨数据中心将多个集群聚合成超大资源池,而非直接采用大规模、统一管理的分布式集群。

功能完善:

Minio支持云原生,能与Kubernetes、Docker、Swarm编排系统良好对接,实现灵活部署。

且部署简单,只有一个可执行文件,参数极少,一条命令即可启动一个Minio系统。

Minio为了高性能采取无元数据数据库设计,避免元数据库成为整个系统的性能瓶颈,

并将故障限制在单个集群之内,从而不会涉及其他集群。Minio同时完全兼容S3接口,

因此也可以作为网关使用,对外提供S3访问。同时使用Minio Erasure code和checksum 来防止硬件故障。

即使损失一半以上的硬盘,但是仍然可以从中恢复。分布式中也允许(N/2)-1个节点故障。

官方文档:

MinIO | 高性能, Kubernetes原生对象存储

Java快速指南:

Java快速指南 — MinIO中文文档 | MinIO Linux中文文档

注:

博客:
霸道流氓气质-CSDN博客

实现

1、Minio在Windows上下载安装启动

MinIO下载和安装 | 用于创建高性能对象存储的代码和下载内容

下载之后只有一个minio.exe,然后新建一个文件存储路径,这里是D:\minioData

在minio.exe所在的目录下打开cmd,输入

minio.exe server D:\minioData

后面跟着指定存储的目录

提示是因为未修改默认密码。

启动之后访问

http://127.0.0.1:9000/

这里会自动跳转到11466端口。

输入登录用户名密码,默认都为

minioadmin

2、SpringBoot中整合Minio实现客户端

添加项目依赖

        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.2.2</version>
        </dependency>

新增yml配置文件内容

​
minio:
  server: http://127.0.0.1
  port: 9000
  accessKey: minioadmin
  secretKey: minioadmin

​

配置minio的ip、端口、用户名、密码

然后新建配置类,读取配置文件内容并建立minio连接

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioConfig{

    private String server;

    private int port;

    private String accessKey;

    private String secretKey;

    /**
     * 创建minio连接对象
     * @return
     */
    @Bean
    public MinioClient minioClient(){
        return  MinioClient.builder()
                .endpoint(server,port,false)
                .credentials(accessKey,secretKey)
                .build();
    }
}

3、SpringBoot中操作Minio的工具类和使用示例

Minio中使用Bucket桶的概念,类似文件目录,一般一个项目中使用一个桶。

新建Minio工具类

import cn.hutool.core.io.FastByteArrayOutputStream;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@Component
public class MinioUtils {

    @Autowired
    MinioClient minioClient;

    public final String PREFIX = "minio/";

    /**
     * 查看存储bucket是否存在
     *  bucketName 需要传入桶名
     * @return boolean
     */
    public Boolean bucketExists(String bucketName) {
        Boolean found;
        try {
            found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return found;
    }

    /**
     * 创建存储bucket
     *  bucketName 需要传入桶名
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 删除存储bucket
     * bucketName 需要传入桶名
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    /**
     * 获取全部bucket
     */
    public List<Bucket> getAllBuckets() {
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            return buckets;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    /**
     * 文件上传
     *
     * @param file 文件
     * @param bucketName bucketName
     * BucketName 需要传入桶名
     * @return Boolean
     */
    public String upload(String bucketName,MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)){
            throw new RuntimeException();
        }
        String fileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
        String objectName = PREFIX + fileName;
        try {
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(objectName)
                    .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectName;
    }

    /**
     * 预览
     * @param fileName
     * @param bucketName bucketName
     * @return
     */
    public String preview(String bucketName,String fileName){
        // 查看文件地址
        GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(bucketName).object(fileName).method(Method.GET).build();
        try {
            String url = minioClient.getPresignedObjectUrl(build);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件下载
     * @param fileName 文件名称
     * @param bucketName bucketName
     * @param res response
     * @return Boolean
     */
    public void download(String bucketName,String fileName, HttpServletResponse res) {
        GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
                .object(fileName).build();
        try (GetObjectResponse response = minioClient.getObject(objectArgs)){
            byte[] buf = new byte[1024];
            int len;
            try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()){
                while ((len=response.read(buf))!=-1){
                    os.write(buf,0,len);
                }
                os.flush();
                byte[] bytes = os.toByteArray();
                res.setCharacterEncoding("utf-8");
                // 设置强制下载不打开
                // res.setContentType("application/force-download");
                res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
                try (ServletOutputStream stream = res.getOutputStream()){
                    stream.write(bytes);
                    stream.flush();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查看文件对象
     * @param bucketName bucketName
     * @return 存储bucket内文件对象信息
     */
    public List<Item> listObjects(String bucketName) {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        List<Item> items = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return items;
    }

    /**
     * 删除文件
     * @param fileName
     * @param bucketName
     * @return
     * @throws Exception
     */
    public boolean remove(String bucketName,String fileName){
        try {
            minioClient.removeObject( RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        }catch (Exception e){
            return false;
        }
        return true;
    }
}

单元测试,引入工具类

    @Autowired
    private MinioUtils minioUtils;

测试新建桶

    /**
     * 创建存储bucket
     */
    @Test
    public void createBucke(){
        List<Bucket> allBuckets = minioUtils.getAllBuckets();
        allBuckets.stream().forEach(bucket -> System.out.println(bucket.name()));
        minioUtils.makeBucket("new");
        allBuckets = minioUtils.getAllBuckets();
        allBuckets.stream().forEach(bucket -> System.out.println(bucket.name()));
    }

测试获取全部桶

    /**
     * 获取全部bucket
     */
    @Test
    public void getAllBuckets() {
        List<Bucket> allBuckets = minioUtils.getAllBuckets();
        System.out.println(allBuckets);
    }

测试桶是否存在

    /**
     * 测试桶是否存在
     */
    @Test
    public void bucketExists() {
        System.out.println(minioUtils.bucketExists("badao"));//true
        System.out.println(minioUtils.bucketExists("test"));//false
    }

测试移除桶

    /**
     * 移除bucket
     */
    @Test
    public void removeBucke(){
        List<Bucket> allBuckets = minioUtils.getAllBuckets();
        allBuckets.stream().forEach(bucket -> System.out.println(bucket.name()));
        minioUtils.removeBucket("new");
        allBuckets = minioUtils.getAllBuckets();
        allBuckets.stream().forEach(bucket -> System.out.println(bucket.name()));
    }

测试文件上传,这里使用MockMultipartFile模拟文件

    /**
     * upload file
     */
    @Test
    public void upload(){
        //MockmulpipartFile
        //第一个参数是表单中文件上传的字段名
        //第二个参数是文件名
        //第三个参数是文件的MIME类型
        //第四个参数是文件的内容
        MultipartFile mockFile = new MockMultipartFile("file","badao.txt","text/plain","File content".getBytes());
        minioUtils.upload("badao",mockFile);
    }

Java中MockMultipartFile使用来模拟文件的用法

MockmulpipartFile

第一个参数是表单中文件上传的字段名

第二个参数是文件名

第三个参数是文件的MIME类型

第四个参数是文件的内容

上传成功查看效果,可进行预览、下载等操作。

此时到minio的存储路径中也能看到文件

测试测试Minio预览

    /**
     * preview file
     */
    @Test
    public void preview(){
        String prefix = minioUtils.PREFIX;
        String preview = minioUtils.preview("badao", prefix+"cd6b22b2-55ce-41ea-ba6f-a9115e12a2fe.txt");
        System.out.println(preview);
    }

预览效果

测试Minio下载效果

import com.ruoyi.common.utils.MinioUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;

@RestController
@RequestMapping("/test/minio")
public class MinioTestController {

    @Autowired
    private MinioUtils minioUtils;

    @GetMapping("/download")
    public void download(HttpServletResponse response) {
        String prefix = minioUtils.PREFIX;
        minioUtils.download("badao",prefix+"cd6b22b2-55ce-41ea-ba6f-a9115e12a2fe.txt",response);
    }
}

请求接口测试效果

更多功能和使用参考官方文档说明。

  • 18
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在Spring Boot项目集成Minio,您可以按照以下步骤进行操作: 1. 首先,创建一个Spring Boot项目并引入Minio的依赖。在您的项目的pom.xml文件添加以下依赖: ```xml <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.0.2</version> </dependency> ``` 2. 接下来,您需要编写Minio的配置文件。您可以在配置文件指定Minio的连接信息(如端口、Access Key和Secret Key等)。 3. 当您完成配置文件的编写后,您可以启动Minio服务。您可以使用以下命令启动Minio服务: ```bash bash run.sh ``` 4. 您可以使用以下命令查看Minio的日志: ```bash tail -f /root/logs/minio/minio.log ``` 5. 最后,您可以在您的Spring Boot项目使用Minio的API进行文件存储和检索操作。您可以参考Minio的官方文档以了解更多关于如何使用Minio的API的信息。 需要注意的是,以上步骤仅是简述了在Linux单节点上部署Minio的过程,具体的部署方式和配置可能会因环境而异。您可以根据您的实际情况进行相应的调整。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SpringBoot整合minio服务(超详细)](https://blog.csdn.net/yueyue763184/article/details/131147025)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [springboot集成Minio](https://blog.csdn.net/AYANBAO/article/details/130031830)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

霸道流氓气质

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值