0
点赞
收藏
分享

微信扫一扫

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


场景

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

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

参考上面搭建项目。

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中文文档

注:

博客:霸道流氓气质

实现

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

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

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

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

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

minio.exe server D:\minioData

后面跟着指定存储的目录

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

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

启动之后访问

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类型

第四个参数是文件的内容

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

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

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

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

测试测试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);
    }

预览效果

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

测试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);
    }
}

请求接口测试效果

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

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

举报

相关推荐

0 条评论