springboot 对接 minio 分布式文件系统
作者:mmseoamin日期:2023-12-21

1. minio介绍

Minio 是一个基于Go语言的对象存储服务。它实现了大部分亚马逊S3云存储服务接口,可以看做是是S3的开源版本,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。区别于分布式存储系统,minio的特色在于简单、轻量级,对开发者友好,认为存储应该是一个开发问题而不是一个运维问题。

2. minio下载地址

下载

3. liunx minio文件授权

chomd +x minio

springboot 对接 minio 分布式文件系统,第1张

4. 编写启动minio shell文件

vi run.sh

#!/bin/bash
#web管理界面登录用户
export MINIO_ROOT_USER=minio
#web管理界面登录密码
export MINIO_ROOT_PASSWORD=minio
#生成共享链接时,需要配置,否则是本地地址127.0.0.1,有地址的可以修改为自己地址
export MINIO_SERVER_URL=http://IP:9002
# nohup启动服务 指定文件存放路径 /root/data 还有设置日志文件路径 /root/minio/log
nohup ./minio server --address :9002 --console-address :9001 /root/data/minio > /root/logs/minio.log 2>&1 &

5. 赋权限给予shell文件run.sh文件

 chmod u+x run.sh

springboot 对接 minio 分布式文件系统,第2张.

6. 启动minio服务,执行sh文件

bash run.sh

springboot 对接 minio 分布式文件系统,第3张

7. 查看日志,我们在4的时候最后一条上有配置log地址

tail -f /root/logs/minio.log

springboot 对接 minio 分布式文件系统,第4张

springboot 对接 minio 分布式文件系统,第5张

8. 浏览器访问minio界面,并且输入在第四步配置的账号密码

springboot 对接 minio 分布式文件系统,第6张

9. 接下来我们可以创建一个我们作为测试的文件桶

springboot 对接 minio 分布式文件系统,第7张

 springboot 对接 minio 分布式文件系统,第8张

 10. 当我们创建好桶之后,我们可以前往查看是否存在

springboot 对接 minio 分布式文件系统,第9张

11. 点击桶进入,手动测试上传文件

springboot 对接 minio 分布式文件系统,第10张

12. 上传文件之后我们可以选择某一个文件进行下载或者链接共享,链接共享默认时间为7天

springboot 对接 minio 分布式文件系统,第11张

13. 当我们点击共享时,会出现一个共享链接,我们可以直接在浏览器内查看相对应的文件

springboot 对接 minio 分布式文件系统,第12张

springboot 对接 minio 分布式文件系统,第13张

14. springboot 对接minio,加入POM文件

  
            io.minio
            minio
            7.1.0
        

15. 配置application文件

生成请求账号密码

springboot 对接 minio 分布式文件系统,第14张

springboot 对接 minio 分布式文件系统,第15张

minio:
  endpoint: http://IP:9002
  accessKey: bjdZxvMDxAzYETgYn0aY 配置账号
  secretKey: uk7srkLHsYkwzvTYVzDBtwzlXz5fxsoMmNpbb3SN 配置密码
  bucketName: test 桶名称-默认

16.springboot 工具类

package com.project.google.util;
/**
 * @Description: TODO
 * @Author xgp
 * @Date 2023/8/7 8:05
 * @PackageName:com.project.google.util
 * @ClassName: MinioTemplate
 * @Version 1.0
 */
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
 * Minio 基础操作类
 *
 * @author: zhanghuaiyu
 * @since 2021-01-22 16:27
 */
@Configuration
public class MinioTemplate implements InitializingBean {
    private MinioClient minioClient;
    @Value("${minio.endpoint}")
    private String url;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;
    @Override
    public void afterPropertiesSet() {
        Assert.hasText(url, "Minio url 为空");
        Assert.hasText(accessKey, "Minio accessKey为空");
        Assert.hasText(secretKey, "Minio secretKey为空");
        this.minioClient = new MinioClient(url, accessKey, secretKey);
    }
    /**
     * 创建bucket
     * setBucketPolicy 设置权限才可以预览
     *
     * @param bucketName bucket名称
     */
    @SneakyThrows
    public Boolean createBucket(String bucketName) {
        if (!bucketExists(bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            StringBuilder builder = new StringBuilder();
            builder.append("{\n");
            builder.append("    \"Statement\": [\n");
            builder.append("        {\n");
            builder.append("            \"Action\": [\n");
            builder.append("                \"s3:GetBucketLocation\",\n");
            builder.append("                \"s3:ListBucket\"\n");
            builder.append("            ],\n");
            builder.append("            \"Effect\": \"Allow\",\n");
            builder.append("            \"Principal\": \"*\",\n");
            builder.append("            \"Resource\": \"arn:aws:s3:::bucketname\"\n");
            builder.append("        },\n");
            builder.append("        {\n");
            builder.append("            \"Action\": \"s3:GetObject\",\n");
            builder.append("            \"Effect\": \"Allow\",\n");
            builder.append("            \"Principal\": \"*\",\n");
            builder.append("            \"Resource\": \"arn:aws:s3:::my-bucketname/*.*\"\n");
            builder.append("        }\n");
            builder.append("    ],\n");
            builder.append("    \"Version\": \"2012-10-17\"\n");
            builder.append("}\n");
            minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(builder.toString().replace("bucketname", bucketName)).build());
            return true;
        } else {
            return false;
        }
    }
    /**
     * 获取全部bucket
     * 

* https://docs.minio.io/cn/java-client-api-reference.html#listBuckets */ @SneakyThrows public List getAllBuckets() { return minioClient.listBuckets(); } /** * 根据bucketName获取信息 * * @param bucketName bucket名称 */ @SneakyThrows public Optional getBucket(String bucketName) { return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst(); } /** * 根据bucketName删除信息 * * @param bucketName bucket名称 */ @SneakyThrows public void removeBucket(String bucketName) { minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); } /** * 根据文件前置查询文件 * * @param bucketName bucket名称 * @param prefix 前缀 * @param recursive 是否递归查询 * @return MinioItem 列表 */ @SneakyThrows public List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) { List list = new ArrayList<>(); Iterable> objectsIterator = minioClient.listObjects(bucketName, prefix, recursive); if (objectsIterator != null) { Iterator> iterator = objectsIterator.iterator(); if (iterator != null) { while (iterator.hasNext()) { Result result = iterator.next(); Item item = result.get(); list.add(item); } } } return list; } /** * 获取文件外链 * * @param bucketName bucket名称 * @param objectName 文件名称 * @param expires 过期时间 <=7 * @return url */ @SneakyThrows public String getObjectUrl(String bucketName, String objectName, Integer expires) { return minioClient.presignedGetObject(bucketName, objectName, expires); } /** * 获取文件路径 * * @param bucketName * @param fileName * @return */ @SneakyThrows public String getObjectUrl(String bucketName, String fileName) { return minioClient.getObjectUrl(bucketName, fileName); } /** * 获取文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @return 二进制流 */ @SneakyThrows public InputStream getObject(String bucketName, String objectName) { return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 获取文件 * * @param bucketName * @param objectName * @return */ @SneakyThrows public ObjectStat statObject(String bucketName, String objectName) { return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 上传文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject */ public String putObject(String bucketName, String objectName, MultipartFile file) throws Exception { if (!this.bucketExists(bucketName)) { this.createBucket(bucketName); } minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(file.getInputStream(), file.getSize(), PutObjectArgs.MIN_MULTIPART_SIZE).contentType(file.getContentType()).build()); return bucketName; } /** * 上传文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @param stream 文件流 * @param size 大小 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject */ public void putObject(String bucketName, String objectName, InputStream stream, long size) throws Exception { minioClient.putObject(bucketName, objectName, stream, new PutObjectOptions(stream.available(), -1)); } /** * 获取文件信息, 如果抛出异常则说明文件不存在 * * @param bucketName bucket名称 * @param objectName 文件名称 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject */ public ObjectStat getObjectInfo(String bucketName, String objectName) throws Exception { return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 删除文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject */ public void removeObject(String bucketName, String objectName) throws Exception { minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); } /** * 批量删除文件夹内所有文件 * * @param bucketName bucket名称 * @param objectName 文件名称 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject */ public void removeObjects(String bucketName, String objectName) throws Exception { List list = getAllObjectsByPrefix(bucketName, objectName, false); for (Item item : list) { removeObject(bucketName, item.objectName()); } } @SneakyThrows public boolean bucketExists(String bucketName) { return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); } /** * 文件下载 * * @param response * @param bucket * @param objectName * @param outName * @throws Exception */ public void download(HttpServletResponse response, String bucket, String objectName, String outName) throws Exception { ObjectStat stat = this.statObject(bucket, objectName); response.setContentType(stat.contentType()); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outName, "UTF-8")); response.setHeader("FileName", URLEncoder.encode(outName, "UTF-8")); InputStream in = this.getObject(bucket, objectName); IOUtils.copy(in, response.getOutputStream()); in.close(); } /** * 合并分片文件到指定目录 * * @param bucket * @param fileName * @param sources * @return * @throws Exception */ public ObjectWriteResponse composeObject(String bucket, String fileName, List sources) throws Exception { ObjectWriteResponse response = minioClient.composeObject(ComposeObjectArgs.builder() .bucket(bucket) .object(fileName) .sources(sources) .build()); return response; } }

 17.请求测试controller方法

package com.project.google.controller;
import afu.org.checkerframework.checker.oigj.qual.O;
import com.project.google.util.MinioTemplate;
import io.minio.messages.Bucket;
import org.apache.commons.io.IOUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
 * @Description: TODO
 * @Author xgp
 * @Date 2023/8/7 8:35
 * @PackageName:com.project.google.controller
 * @ClassName: TbMinioController
 * @Version 1.0
 */
@RestController
public class TbMinioController {
    @Autowired
    private MinioTemplate minioTemplate;
    //创建新的桶
    @GetMapping("createBucket")
    public Object createBucket(String bucketName){
        return minioTemplate.createBucket(bucketName);
    }
    //获取对应桶信息
    @GetMapping("getList")
    public Object getList(String bucketName){
        Bucket bucket = minioTemplate.getBucket(bucketName).get();
        Map map = new HashMap<>();
        map.put("name",bucket.name());
        map.put("createDate",bucket.creationDate());
        return map;
    }
    //获取所有桶信息
    @GetMapping("getAll")
    public Object getAll(){
        List list = new ArrayList<>();
        List buckets = minioTemplate.getAllBuckets();
        buckets.stream().forEach(item -> {
            Map map = new HashMap<>();
            map.put("name",item.name());
            map.put("createDate",item.creationDate());
            list.add(map);
        });
        return list;
    }
    /**上传文件到对应桶里,如果你想放入指定文件夹,传入文件名前面带上文件夹名称及路径,比如 test文件夹就- test/文件名,依次类推*/
    @PostMapping("uploadFile")
    public Object uploadFile(@RequestParam("file") MultipartFile file) throws Exception {
        /**文件夹属性,可加载文件前方*/
        return minioTemplate.putObject("test","uu/" + file.getOriginalFilename(),file);
    }
    /**获取图片信息,二进制数据转换为图片呈现*/
    @GetMapping(value = "getFile", produces = MediaType.IMAGE_JPEG_VALUE)
    public byte[] getFile(@RequestParam("bucketName") String bucketName
            ,@RequestParam("objectName") String objectName) throws IOException {
        InputStream stream = minioTemplate.getObject(bucketName, objectName);
        byte[] bytes = IOUtils.toByteArray(stream);
        return bytes;
    }
    /**获取图片分享链接,expires为过期时间,可为小于等于7*/
    @GetMapping(value = "getObjectUrl")
    public String getObjectUrl(@RequestParam("bucketName") String bucketName
            ,@RequestParam("objectName") String objectName) throws IOException {
        return minioTemplate.getObjectUrl(bucketName, objectName, 1);
    }
}

18.接口测试

18.1 查询test bucket信息

springboot 对接 minio 分布式文件系统,第16张

 

18.2 获取所有bucket信息

springboot 对接 minio 分布式文件系统,第17张

18.3 上传文件,我这边是通过apipox进行测试

springboot 对接 minio 分布式文件系统,第18张

springboot 对接 minio 分布式文件系统,第19张

18.4 查看图片信息

springboot 对接 minio 分布式文件系统,第20张

18.5 生成共享链接

springboot 对接 minio 分布式文件系统,第21张springboot 对接 minio 分布式文件系统,第22张

到此,整个对接过程就已经差不多了,其他扩展功能,如有需要,可以咨询这边,给出解答或者思路。