springboot整合minio的超详细教程

 更新时间:2023年12月19日 09:22:11   作者:逆风飞翔的小叔  
在很多互联网产品应用中,都涉及到各种与文件存储相关的业务,随着技术的发展,关于如何解决分布式文件存储也有了比较成熟的方案,比如私有云部署下可以考虑fastdfs,阿里云对象存储oss,七牛云等,本篇将为你介绍另一种文件存储方式,即MinIO,需要的朋友可以参考下

一、前言

在很多互联网产品应用中,都涉及到各种与文件存储相关的业务,随着技术的发展,关于如何解决分布式文件存储也有了比较成熟的方案,比如私有云部署下可以考虑fastdfs,阿里云对象存储oss,七牛云等,本篇将为你介绍另一种文件存储方式,即MinIO 。

二、Minio 概述

2.1 Minio简介

MinIO基于Apache License v2.0开源协议的对象存储服务,可以做为云存储的解决方案用来保存海量的图片,视频,文档,是一款高性能、分布式的对象存储系统, 可以100%的运行在标准硬件,即X86等低成本机器也能够很好的运行MinIO。

传统的存储和其他的对象存储不同的是:

它一开始就针对性能要求更高的私有云标准进行软件架构设计。因为MinIO一开始就只为对象存储而设计。所以他采用了更易用的方式进行设计,它能实现对象存储所需要的全部功能,在性能上也更加强劲,它不会为了更多的业务功能而妥协,失去MinIO的易用性、高效性。 这样的结果所带来的好处是:它能够更简单的实现局有弹性伸缩能力的原生对象存储服务。

2.2 Minio特点

Minio具有如下特点

  • 性能高,准硬件条件下它能达到55GB/s的读、35GB/s的写速率;
  • 部署自带管理界面;
  • MinIO.Inc运营的开源项目,社区活跃度高;
  • 提供了所有主流开发语言的SDK;
  • 基于Golang语言实现,配置简单,单行命令可以运行起来;
  • 兼容亚马逊S3云存储服务接口,适合于存储大容量非结构化的数据,一个对象文件可以是任意大小,从几kb到最大5T不等;

三、Minio 环境搭建

本文采用docker的方式快速搭建起Minio的环境,也可以通过官网下载安装包部署,官网安装包下载地址

3.1 部署过程

3.1.1 拉取镜像

docker pull minio/minio

3.1.2 启动容器

docker run -d -p 9000:9000 -p 9090:9090 \
     --name minio \
     -e "MINIO_ACCESS_KEY=minio" \
     -e "MINIO_SECRET_KEY=minio" \
     -v /home/minio/data:/data \
     -v /home/minio/config:/root/.minio \
     minio/minio server \
     /data --console-address ":9090" -address ":9000"

3.1.3 访问web页面

容器启动成功后,注意开发相关的防火墙端口即可,然后访问地址:IP:9000,即可访问Minio的web界面

输入账户和密码,登录进去之后,看到下面的效果说明Minio环境搭建完成

四、Minio基本使用

4.1 基本概念

在正式开始使用Minio之前,有必要先了解下几个相关的概念

  • bucket ,类比于文件系统的目录;
  • Object ,类比文件系统的文件;
  • Keys ,类比文件名;

4.2 上传文件演示

如下,点击创建一个新的bucket,创建完成后就可以在列表上看到这个bucket;

给当前这个bucket上传一个文件

点击文件夹图标

上传一张本地文件,上传完成后就可以看到这个文件了,也可以浏览上传的文件

4.3 用户管理

针对客户端的操作,经常需要维护相关的账号来管理,比如账户的操作权限,访问控制等;

点击,创建用户

填写用户信息,勾选权限保存即可

然后在用户列表就可以看到这个用户了

4.4 Java操作Minio

通过上面的环境搭建和操作,演示并了解了如何快速使用Minio,更多的功能可以参阅相关资料进一步学习了解,下面我们编写java代码完成文件的上传。

4.4.1 导入依赖

在当前的maven工程中导入minio的依赖,客户端具体版本可以根据你的实际需要选择

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

4.4.2 上传文件到minio

通过下面这段代码将本地的一张图片文件上传到minio的test这个bucket目录下

public static void main(String[] args) {
        FileInputStream inputStream = null;
        try {
            inputStream = new FileInputStream("F:\\网盘\\Title-logo.png");
            MinioClient client = MinioClient.builder().credentials("minio", "minio").endpoint("http://IP:9000").build();
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object("logo.png")
                    .contentType("image/png")
                    .bucket("test")
                    .stream(inputStream, inputStream.available(), -1)
                    .build();
            client.putObject(putObjectArgs);
            System.out.println("上传成功");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
 
    }

运行这段代码,上传成功后,去到test的目录下刷新之后可以看到文件已经上传

五、springboot整合Minio

接下来让我们看看如何在springboot中集成Minio,参考下面的操作步骤

5.1 前置准备

5.1.1 引入依赖

创建一个maven工程,引入如下相关的依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
    </parent>
 
 
    <dependencies>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
 
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>7.1.0</version>
        </dependency>
 
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
 
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
 
    </dependencies>

5.1.2 核心配置文件

在配置文件中添加如下内容

server:
  port: 8087
 
logging:
  level:
    root: info
    com.congge.model: debug
 
minio:
  username: minio
  pwd: minio
  bucket: test
  endpoint: http://IP:9000
  readPath: http://IP:9000

5.2 编码过程

5.2.1 创建一个参数配置类

通过这种方式将配置文件中以minio开头的那些配置加载到spring容器中管理,其他位置使用的时候直接注入即可

@Data
@ConfigurationProperties(prefix = "minio")
@Component
public class MinIOConfigProperties implements Serializable {
 
    private String username;
    private String pwd;
    private String bucket;
    private String endpoint;
    private String readPath;
 
}

5.2.2 创建minio配置类

通过这个全局的配置类,其他需要上传文件的类中直接注入MinioClient即可

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Data
@Configuration
public class MinIOConfig {
 
    @Autowired
    private MinIOConfigProperties minIOConfigProperties;
 
    @Bean
    public MinioClient buildMinioClient() {
        return MinioClient
                .builder()
                .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
                .endpoint(minIOConfigProperties.getEndpoint())
                .build();
    }
}

5.2.3 创建minio文件服务类或工具类

在日常开发中,可以自定义一个minio的工具类,使用的时候就比较方便了,下面的代码中列举了常用的一些API操作方法,可以结合实际需要自定义更多的工具方法

 
import com.congge.config.MinIOConfig;
import com.congge.config.MinIOConfigProperties;
import com.congge.service.MinioFileService;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
 
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
 
@Slf4j
@Import(MinIOConfig.class)
@Service
public class MinioFileServiceImpl implements MinioFileService {
 
    @Autowired
    private MinioClient minioClient;
 
    @Autowired
    private MinIOConfigProperties minIOConfigProperties;
 
    private final static String separator = "/";
 
    /**
     * 下载文件
     *
     * @param pathUrl 文件全路径
     * @return 文件流
     */
    @Override
    public void downLoadFile(String pathUrl, HttpServletResponse response) {
        String[] pathItems = pathUrl.split("/");
        String originFileName = pathItems[pathItems.length - 1];
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
        int index = key.indexOf(separator);
        //String bucket = key.substring(0,index);
        String filePath = key.substring(index + 1);
        InputStream inputStream = null;
        try {
            inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build());
            response.setHeader("Content-Disposition", "attachment;filename=" + originFileName);
            response.setContentType("application/force-download");
            response.setCharacterEncoding("UTF-8");
            IOUtils.copy(inputStream, response.getOutputStream());
            System.out.println("下载成功");
        } catch (Exception e) {
            log.error("minio down file error.  pathUrl:{}", pathUrl);
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    @Override
    public String uploadFile(MultipartFile file) throws Exception {
        String bucketName = minIOConfigProperties.getBucket();
        String endpoint = minIOConfigProperties.getEndpoint();
 
        // 检查存储桶是否已经存在
        boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (isExist) {
            System.out.println("Bucket already exists.");
        } else {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
        String originalFilename = file.getOriginalFilename();
        //拼接生成新的UUID形式的文件名
        String objectName = new SimpleDateFormat("yyyy/MM/dd/").format(new Date()) +
                UUID.randomUUID().toString().replaceAll("-", "")
                + originalFilename.substring(originalFilename.lastIndexOf("."));
 
        PutObjectArgs objectArgs = PutObjectArgs.builder().object(objectName)
                .bucket(bucketName)
                .contentType(file.getContentType())
                .stream(file.getInputStream(), file.getSize(), -1).build();
        minioClient.putObject(objectArgs);
        //组装桶中文件的访问url
        String resUrl = endpoint + "/" + bucketName + "/" + objectName;
        return resUrl;
    }
 
    /**
     * 删除文件
     *
     * @param pathUrl 文件全路径
     */
    @Override
    public void delete(String pathUrl) {
        String key = pathUrl.replace(minIOConfigProperties.getEndpoint() + "/", "");
        int index = key.indexOf(separator);
        String bucket = key.substring(0, index);
        String filePath = key.substring(index + 1);
        // 删除Objects
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
        try {
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("minio remove file error.  pathUrl:{}", pathUrl);
            e.printStackTrace();
        }
    }
 
    public List<Bucket> listBuckets()
            throws Exception {
        return minioClient.listBuckets();
    }
 
    public boolean bucketExists(String bucketName) throws Exception {
        boolean flag = minioClient.bucketExists(bucketName);
        if (flag) {
            return true;
        }
        return false;
    }
 
    @Override
    public List<String> listBucketNames() throws Exception{
        List<Bucket> bucketList = listBuckets();
        List<String> bucketListName = new ArrayList<>();
        for (Bucket bucket : bucketList) {
            bucketListName.add(bucket.name());
        }
        return bucketListName;
    }
 
    @Override
    public boolean makeBucket(String bucketName) throws Exception{
        boolean flag = bucketExists(bucketName);
        if (!flag) {
            minioClient.makeBucket(bucketName);
            return true;
        } else {
            return false;
        }
    }
 
    @Override
    public boolean removeBucket(String bucketName) throws Exception{
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable<Result<Item>> myObjects = listObjects(bucketName);
            for (Result<Item> result : myObjects) {
                Item item = result.get();
                // 有对象文件,则删除失败
                if (item.size() > 0) {
                    return false;
                }
            }
            // 删除存储桶,注意,只有存储桶为空时才能删除成功。
            minioClient.removeBucket(bucketName);
            flag = bucketExists(bucketName);
            if (!flag) {
                return true;
            }
 
        }
        return false;
    }
 
    @Override
    public List<String> listObjectNames(String bucketName) throws Exception{
        List<String> listObjectNames = new ArrayList<>();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            Iterable<Result<Item>> myObjects = listObjects(bucketName);
            for (Result<Item> result : myObjects) {
                Item item = result.get();
                listObjectNames.add(item.objectName());
            }
        }
        return listObjectNames;
    }
 
    @Override
    public boolean removeObject(String bucketName, String objectName) throws Exception{
        boolean flag = bucketExists(bucketName);
        if (flag) {
            List<String> objectList = listObjectNames(bucketName);
            for (String s : objectList) {
                if(s.equals(objectName)){
                    minioClient.removeObject(bucketName, objectName);
                    return true;
                }
            }
        }
        return false;
    }
 
    @Override
    public String getObjectUrl(String bucketName, String objectName) throws Exception{
        boolean flag = bucketExists(bucketName);
        String url = "";
        if (flag) {
            url = minioClient.getObjectUrl(bucketName, objectName);
        }
        return url;
    }
 
    public Iterable<Result<Item>> listObjects(String bucketName) throws Exception {
        boolean flag = bucketExists(bucketName);
        if (flag) {
            return minioClient.listObjects(bucketName);
        }
        return null;
    }
 
 
}

5.2.4 编写测试接口

为了方便测试,下面定义了一个测试接口

 
import com.congge.service.MinioFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
 
import javax.servlet.http.HttpServletResponse;
import java.util.List;
 
@RestController
@Slf4j
public class FileController {
 
    @Autowired
    private MinioFileService minioFileService;
 
    /**
     * 上传文件
     * @param file
     * @return
     * @throws Exception
     */
    @PostMapping("/upload")
    public String upload(@RequestBody MultipartFile file) throws Exception {
        String url = minioFileService.uploadFile(file);
        return "文件上传成功,文件路径:" + url;
    }
 
    /**
     * 下载文件
     * @param pathUrl
     * @param response
     */
    @GetMapping("/download")
    public void download(@RequestParam("pathUrl") String pathUrl, HttpServletResponse response) {
        minioFileService.downLoadFile(pathUrl,response);
    }
 
    /**
     * 列出所有bucket名称
     * @return
     * @throws Exception
     */
    @PostMapping("/list/bucket")
    public List<String> list() throws Exception {
        return minioFileService.listBucketNames();
    }
 
 
    /**
     * 创建bucket
     * @param bucketName
     * @return
     * @throws Exception
     */
    @PostMapping("/create/bucket")
    public boolean createBucket(@RequestParam("bucketName")String bucketName) throws Exception {
        return minioFileService.makeBucket(bucketName);
    }
 
    /**
     * 删除bucket
     * @param bucketName
     * @return
     * @throws Exception
     */
    @PostMapping("/delete/bucket")
    public boolean deleteBucket(@RequestParam("bucketName")String bucketName) throws Exception {
        return minioFileService.removeBucket(bucketName);
    }
 
    /**
     * 列出bucket的所有对象名称
     * @param bucketName
     * @return
     * @throws Exception
     */
    @PostMapping("/list/object_names")
    public List<String> listObjectNames(@RequestParam("bucketName")String bucketName) throws Exception {
        return minioFileService.listObjectNames(bucketName);
    }
 
    /**
     * 删除bucket中的某个对象
     * @param bucketName
     * @param objectName
     * @return
     * @throws Exception
     */
    @PostMapping("/remove/object")
    public boolean removeObject(@RequestParam("bucketName")String bucketName,@RequestParam("objectName") String objectName) throws Exception {
        return minioFileService.removeObject(bucketName, objectName);
    }
 
    /**
     * 获取文件访问路径
     * @param bucketName
     * @param objectName
     * @return
     * @throws Exception
     */
    @PostMapping("/get/object/url")
    public String getObjectUrl(@RequestParam("bucketName")String bucketName, @RequestParam("objectName")String objectName) throws Exception {
        return minioFileService.getObjectUrl(bucketName, objectName);
    }
 
}

5.2.5 接口测试

上传文件测试

上传成功后,返回了文件的完整路径,方便后续使用

然后在控制台的test这个bucket中检查是否上传上去了

下载文件测试

使用上一步返回的url,直接调用下载接口,下载完成后可以直接预览

更多的接口有兴趣的同学可以一一尝试下,就不再赘述了。

六、写在文末

本文详细总结了Minio的搭建使用,以及与springboot整合的完整步骤,作为一款适用且轻量级的文件存储服务器,可以私有化部署,也可以很方便进行云上部署、容器化部署,为今后在实际项目中的技术选型提供一个参考,本篇到此结束,感谢观看。

以上就是springboot整合minio的超详细教程的详细内容,更多关于springboot整合minio的资料请关注脚本之家其它相关文章!

相关文章

  • 关于mybatis-plus逻辑删除自动填充更新时间的问题

    关于mybatis-plus逻辑删除自动填充更新时间的问题

    mybatis-plus是对mybatis的增强,mybatis-plus更像是面向对象编程,数据库基本CRUD的操作可以不用手动编写SQL语句,大大提高了开发的效率,这篇文章主要介绍了mybatis-plus逻辑删除自动填充更新时间问题,需要的朋友可以参考下
    2022-07-07
  • kafka运维consumer-groups.sh消费者组管理

    kafka运维consumer-groups.sh消费者组管理

    这篇文章主要为大家介绍了kafka运维consumer-groups.sh消费者组管理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 浅谈一下数据库连接池Druid德鲁伊

    浅谈一下数据库连接池Druid德鲁伊

    数据库连接池就是一个容器持有多个数据库连接,当程序需要操作数据库的时候直接从池中取出连接,使用完之后再还回去,和线程池一个道理,需要的朋友可以参考下
    2023-05-05
  • Apache Dubbo的SPI机制是如何实现的

    Apache Dubbo的SPI机制是如何实现的

    SPI全称为Service Provider Interface,对应中文为服务发现机制。SPI类似一种可插拔机制,首先需要定义一个接口或一个约定,然后不同的场景可以对其进行实现,调用方在使用的时候无需过多关注具体的实现细节。在Java中,SPI体现了面向接口编程的思想,满足开闭设计原则。
    2021-06-06
  • Java实现加盐加密算法的示例详解

    Java实现加盐加密算法的示例详解

    这篇文章主要为大家详细介绍了如何利用Java语言实现加盐加密算法,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
    2023-06-06
  • Java的Cglib动态代理实现方式详解

    Java的Cglib动态代理实现方式详解

    这篇文章主要介绍了Java的Cglib动态代理实现方式详解,CGLIB是强大的、高性能的代码生成库,被广泛应用于AOP框架,它底层使用ASM来操作字节码生成新的类,为对象引入间接级别,以控制对象的访问,需要的朋友可以参考下
    2023-11-11
  • ReentrantLock获取锁释放锁的流程示例分析

    ReentrantLock获取锁释放锁的流程示例分析

    这篇文章主要为大家介绍了ReentrantLock获取锁释放锁的流程示例分析详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • MyBatis逆向工程基本操作及代码实例

    MyBatis逆向工程基本操作及代码实例

    这篇文章主要介绍了MyBatis逆向工程基本操作及代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • 利用Jackson解决Json序列化和反序列化问题

    利用Jackson解决Json序列化和反序列化问题

    Jackson是一个用于处理Json数据的Java库,它提供了一系列功能,包括Json序列化和反序列化,所以本文就来讲讲如何利用利用Jackson解决Json序列化和反序列化的问题吧
    2023-05-05
  • jvm crash的崩溃日志详细分析及注意点

    jvm crash的崩溃日志详细分析及注意点

    本篇文章主要介绍了jvm crash的崩溃日志详细分析及注意点。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-04-04

最新评论