MinIO环境搭建&SpringBoot集成MiniO

一. MiniO

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

文档:MinIO对象存储 Kubernetes — MinIO中文文档 | MinIO Kubernetes中文文档

MinIO是一款分布式文件系统(或者叫对象存储服务),可以做为云存储的解决方案用来保存海量的图片、视频、文档等。由于采用Golang实现,服务端可以工作在Windows、Linux、 OS X和FreeBSD上。配置简单,基本是复制可执行程序,单行命令就可以运行起来。

MinIO兼容亚马逊S3(Simple Storage Service,简单存储服务)云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而且每个对象文件可以是任意大小,从几kb到最大5T不等。

MiniO特点:

1. 高性能:作为高性能对象存储,在标准硬件条件下它能达到55GB/s的读、35GB/s的写速率;
2. 可扩容:不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并跨越多个数据中心;
3. SDK支持: 基于Minio轻量的特点,它得到类似Java、Python或Go等语言的sdk支持;
4. 支持纠删码:MinIO使用纠删码、Checksum来防止硬件错误和静默数据污染。在最高冗余度配置下,即使丢失1/2的磁盘也能恢复数据;

MiniO基本概念:
  • bucket(桶) :类似文件系统的目录(文件夹);
  • Object : 类似文件系统的文件;
  • Keys :类似文件名;
  • MINIO_ACCESS_KEY:访问key,类似账号;
  • MINIO_SECRET_KEY:秘钥,类似密码。

二. MiniO环境搭建

基于Docker搭建MiniO环境

1. 拉取MiniO镜像

下载指定版本的minio
docker pull minio/minio:RELEASE.2021-04-06T23-11-00Z  

2. 创建容器

docker run -p 4007:9000 --name  minio -d --restart=always -e "MINIO_ACCESS_KEY=minio" -e "MINIO_SECRET_KEY=minio123" -v /date/data/minio/data:/data -v /date/data/minio/config:/root/.minio minio/minio:RELEASE.2021-04-06T23-11-00Z server /data
  • -p 4007:9000 ,端口映射
  • -e,环境变量
  • -d,后台运行
  • –name,给容器起名字
  • –restart=always,开机自启
  • -e “MINIO_ACCESS_KEY=minio”,设置账号
  • -e “MINIO_SECRET_KEY=minio123”,设置密码
  • -v 挂载数据卷

3. 查看容器是否启动 :docker ps

4. 访问MinIO后台系统,直接浏览器访问 http://ip:port 即可,账号minio,密码minio123

5. 登录后的管理界面

6. 创建bukect桶,右下角+号点击创建桶

创建一个test桶

三. SpringBoot整合MiniO

1. 在已有的SpringBoot项目中导入MiniO相关依赖

<dependencies>
    <!--minio-->
    <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>7.1.0</version>
    </dependency>
    <!--web-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--test-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <!--knife4j(swagger) -->
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>knife4j-spring-boot-starter</artifactId>
        <version>3.0.2</version>
    </dependency>
    <!--lombok-->
     <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

2. 编写MiniO属性配置类

package com.baidou.dto;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.Serializable;

@Data
@Component
@ConfigurationProperties(prefix = "minio")  //自动注入属性前缀为minio的配置
public class MinIOConfigProperties implements Serializable {

    private String accessKey; // 访问key
    private String secretKey; // 秘钥
    private String bucket;    // 桶
    private String endpoint;  // 地域节点
    private String readPath;  // 读取路径
}

3. 编写MinIO配置类,注册MinioClient客户端的Bean对象

package com.baidou.config;

import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * MinIO配置类
 *
 * @author 白豆五
 * @version 2023/04/21
 * @since JDK8
 */
@Configuration
public class MinIOConfig {

    @Autowired
    private MinIOConfigProperties minIOConfigProperties;

    // 注册MinIO实例
    @Bean
    public MinioClient buildMinioClient(){
        return MinioClient
                .builder()
                .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
                .endpoint(minIOConfigProperties.getEndpoint())
                .build();
    }
}

4. 在application.yml文件中配置minio自定义属性和文件上传大小

minio:
  accessKey: minio
  secretKey: minio123
  bucket: testminio
  endpoint: http://219.159.22.22:4007
  readPath: http://219.159.22.22:4007
  
  servlet:
    multipart:
      # 单个上传文件的最大值是200mb
      max-file-size: 200MB
      # 单次请求的最大值
      max-request-size: 200MB

5. 编写操作MiniO相关业务接口(导包根据自身项目来)

package com.geb.system.service;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;

/**
 * 操作minio相关业务接口
 *
 * @author Jx
 * @version 2023-11-14
 * @since JDK8
 */
public interface IFileStorageService{

    /**
     * 上传文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */
    public String uploadFile(String prefix, String filename, InputStream inputStream);

    /**
     * 删除文件
     *
     * @param pathUrl 文件全路径
     */
    public void delete(String pathUrl);

    /**
     * 下载文件
     *
     * @param pathUrl 文件全路径
     * @return
     */
    public void downLoad(HttpServletResponse response, String pathUrl);


    /**
     * 在线预览文件
     * @param filePath
     * @return
     */
    public String getPreviewUrl(String filePath);

}

实现类:

package com.geb.system.service.impl;


import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.geb.common.config.MinIOConfigProperties;
import com.geb.system.service.IFileStorageService;
import io.minio.*;
import io.minio.http.Method;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

@Slf4j
@Service
public class MinIOFileStorageServiceImpl implements IFileStorageService {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private MinIOConfigProperties minIOConfigProperties;

    private final static String separator = "/"; //文件夹分隔符

    private final static String endPointUrl = "http://219.159.22.22:4007";
    private final static String endPointUrl1 = "http://minio:9000";

    /**
     * 构建文件的绝对路径
     *
     * @param dirPath  文件路径
     * @param filename 文件名  yyyy/mm/dd/file.jpg
     * @return /test
     */
    public String builderFilePath(String dirPath, String filename) {
        StringBuilder stringBuilder = new StringBuilder(50);
        if (!StringUtils.isEmpty(dirPath)) {
            stringBuilder.append(dirPath).append(separator);
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        String todayStr = sdf.format(new Date());
        stringBuilder.append(todayStr).append(separator);
        stringBuilder.append(filename);
        return stringBuilder.toString();
    }


    /**
     * 上传文件
     *
     * @param prefix      文件前缀
     * @param filename    文件名
     * @param inputStream 文件流
     * @return 文件全路径
     */
    @Override
    public String uploadFile(String prefix, String filename, InputStream inputStream) {
        String filePath = builderFilePath(prefix, filename);
        try {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .object(filePath) //文件名
                    .contentType("text/json")//文件类型
                    .bucket(minIOConfigProperties.getBucket())//桶名称与minio创建的桶一致
                    .stream(inputStream, inputStream.available(), -1)//文件流
                    .build();
            minioClient.putObject(putObjectArgs);
            StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath());
            urlPath.append(separator + minIOConfigProperties.getBucket());
            urlPath.append(separator);
            urlPath.append(filePath);
            return urlPath.toString(); //文件全路径
        } catch (Exception ex) {
            log.error("minio put file error.", ex);
            ex.printStackTrace();
            throw new RuntimeException("上传文件失败");
        }
    }

    /**
     * 删除文件
     *
     * @param pathUrl 文件全路径
     */
    @Override
    public void delete(String pathUrl) {
        /** 获取桶后面的删除路径 */
        String delUrl = pathUrl.substring(pathUrl.lastIndexOf("/json"), pathUrl.length());

        /** 重新构建根节点endpoint */
        minioClient = MinioClient.builder()
                .endpoint(endPointUrl1)
                .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
                .build();

        /** 删除文件 */
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs
                .builder()
                .bucket(minIOConfigProperties.getBucket())
                .object(separator + delUrl).build();
        try {
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("minio remove file error.  pathUrl:{}", pathUrl);
            e.printStackTrace();

        }
    }


    /**
     * 下载文件
     *
     * @param pathUrl 文件全路径
     * @return 文件流 byte[]
     */
    @Override
    public void downLoad(HttpServletResponse response, String pathUrl) {/** 拼接完整的下载路径 pathUrl+minio */
        /** 获取文件名称 */
        String fileName = pathUrl.substring(pathUrl.lastIndexOf("/") + 1, pathUrl.length()) + ".json";
        /** 获取桶后面的下载路径 */
        String downUrl = pathUrl.substring(pathUrl.lastIndexOf("/json"), pathUrl.length());

        /** 获取本地文件默认下载路径 */
        InputStream inputStream = null;
        try {
            /** 重新构建根节点endpoint */
            minioClient = MinioClient.builder()
                    .endpoint(endPointUrl1)
                    .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
                    .build();

            inputStream = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(minIOConfigProperties.getBucket())
                    .object(downUrl).build());

            /*** text/json */
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.setCharacterEncoding("UTF-8");

            /*** 用文件流的方式读取到下载文件中 */
            OutputStream outputStream = response.getOutputStream();
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            inputStream.close();
            outputStream.close();
        } catch (Exception e) {
            log.error("minio down file error.  pathUrl:{}", pathUrl);
            e.printStackTrace();
        }
    }



    /**
     * 在线预览文件
     * @param pathUrl
     * @return
     */
    @Override
    @SneakyThrows(Exception.class)
    public String getPreviewUrl(String pathUrl) {
        /** 获取文件名称 */
        String fileName = pathUrl.substring(pathUrl.lastIndexOf("/") + 1, pathUrl.length());
        /** 获取桶后面的下载路径 */
        String downUrl = pathUrl.substring(pathUrl.lastIndexOf("/json"), pathUrl.length());
        /** 重新构建根节点endpoint */
        minioClient = MinioClient.builder()
                .endpoint(endPointUrl)
                .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey())
                .build();

        /** 获取预览的路径 */
        String url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(minIOConfigProperties.getBucket())
                        .object(downUrl)
                        .build());
        System.out.println("previewUrl===" + url);
        return url;
    }








}

以上主要是MiniO业务的一些常规操作,实现类里面的具体路径以及桶需要的操作路径,由于我是docker安装的MiniO服务器,都放在同一台服务器上可能会有影响,路径可以自行处理下!

5. 编写统一返回结果类

package com.geb.common.core.domain;

import java.io.Serializable;
import com.geb.common.constant.HttpStatus;

/**
 * 响应信息主体
 *
 * @author ruoyi
 */
public class R<T> implements Serializable
{
    private static final long serialVersionUID = 1L;

    /** 成功 */
    public static final int SUCCESS = HttpStatus.SUCCESS;

    /** 失败 */
    public static final int FAIL = HttpStatus.ERROR;

    private int code;

    private String msg;

    private T data;

    public static <T> R<T> ok()
    {
        return restResult(null, SUCCESS, "操作成功");
    }

    public static <T> R<T> ok(T data)
    {
        return restResult(data, SUCCESS, "操作成功");
    }

    public static <T> R<T> ok(T data, String msg)
    {
        return restResult(data, SUCCESS, msg);
    }

    public static <T> R<T> fail()
    {
        return restResult(null, FAIL, "操作失败");
    }

    public static <T> R<T> fail(String msg)
    {
        return restResult(null, FAIL, msg);
    }

    public static <T> R<T> fail(T data)
    {
        return restResult(data, FAIL, "操作失败");
    }

    public static <T> R<T> fail(T data, String msg)
    {
        return restResult(data, FAIL, msg);
    }

    public static <T> R<T> fail(int code, String msg)
    {
        return restResult(null, code, msg);
    }

    private static <T> R<T> restResult(T data, int code, String msg)
    {
        R<T> apiResult = new R<>();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }

    public int getCode()
    {
        return code;
    }

    public void setCode(int code)
    {
        this.code = code;
    }

    public String getMsg()
    {
        return msg;
    }

    public void setMsg(String msg)
    {
        this.msg = msg;
    }

    public T getData()
    {
        return data;
    }

    public void setData(T data)
    {
        this.data = data;
    }

    public static <T> Boolean isError(R<T> ret)
    {
        return !isSuccess(ret);
    }

    public static <T> Boolean isSuccess(R<T> ret)
    {
        return R.SUCCESS == ret.getCode();
    }
}

6. 编写Controller,里面设计到的@Api注解是安装配置了Swagger接口文档,详情可见

IDEA配置Swagger+统一返回数据嵌套展示信息(不被拦截器拦截处理)_swagger3 统一的返回封装类-CSDN博客

package com.baidou.controller;

import com.baidou.dto.Result;
import com.baidou.service.FileStorageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.UUID;

/**
 * 操作minio的控制器类
 *
 * @author 白豆五
 * @version 2023/04/21
 * @since JDK8
 */
@RestController
@RequestMapping("/minio")
@Api(tags = "minio相关接口")
public class MinioController {

    @Autowired
    private FileStorageService fileStorageService;


    /**
     * 上传图片到minio
     *
     * @param file
     * @return
     */
    @PostMapping("/uploadFile")
    @ApiOperation(value = "手动上传json文件接口--给算法提供")
    public R<String> uploadFile(MultipartFile file) {
        // 获取文件名称
        String fileName = file.getOriginalFilename();
        fileName = fileName.substring(0, fileName.indexOf(".json"));
        QueryWrapper<DataFormatConversion> wrapper = new QueryWrapper<>();
        wrapper.eq("data_name", fileName);
        List<DataFormatConversion> dataFormatConversions = dataFormatConversionService.list(wrapper);
        if(!dataFormatConversions.isEmpty()){
            throw new ServiceException(BaseErrorCodeEnum.CODE_ERROR_DATA_SAME_ERROR.getMessage(), BaseErrorCodeEnum.CODE_ERROR_DATA_SAME_ERROR.getCode());
        }
        try {
            // 获取文件输入流
            InputStream is = file.getInputStream();
            String fileUrl = fileStorageService.uploadFile("json", fileName, is);
            System.out.println("fileUrl:" + fileUrl);
            /** 更新产线可以访问的文件路径 */
            if(fileUrl.contains(minIOConfigProperties.getEndpoint())){
                fileUrl = fileUrl.replace(minIOConfigProperties.getEndpoint(), pointIP);
            }
            dataFormatConversionService.insertData(fileName, fileUrl);
            return R.ok(fileUrl);
        } catch (IOException e) {
            e.printStackTrace();
            return R.fail();
        }
    }
}

7. 启动项目后用postman调用接口测试

{{host}}变量是在postsman里面配置的环境,可直接用IP:Port替换掉即可

具体的上传文件类型,可自行设置更改,以此记录~

  • 44
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当将MinIOSpring Boot集成时,您可以使用MinIO Java客户端库来与MinIO对象存储系统进行交互。以下是一些步骤可以帮助您开始整合: 1. 首先,您需要在Spring Boot项目的依赖项中添加MinIO Java客户端库。在您的`pom.xml`文件中添加以下依赖项: ```xml <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.0.6</version> </dependency> ``` 2. 创建一个配置类来配置MinIO连接和操作。在您的Spring Boot项目中创建一个名为`MinioConfig`的类,并添加以下代码: ```java @Configuration public class MinioConfig { @Value("${minio.accessKey}") private String accessKey; @Value("${minio.secretKey}") private String secretKey; @Value("${minio.endpoint}") private String endpoint; @Bean public MinioClient minioClient() { return MinioClient.builder() .endpoint(endpoint) .credentials(accessKey, secretKey) .build(); } } ``` 在上面的代码中,我们使用`@Value`注解从配置文件中获取MinIO的访问密钥、秘钥和端点信息,并创建一个`MinioClient` bean。 3. 在需要使用MinIO的地方注入`MinioClient`并进行操作。例如,如果您想上传文件到MinIO,可以创建一个服务类并注入`MinioClient`,然后编写上传文件的方法: ```java @Service public class MinioService { private final MinioClient minioClient; public MinioService(MinioClient minioClient) { this.minioClient = minioClient; } public void uploadFile(String bucketName, String objectName, String filePath) { try { minioClient.uploadObject(UploadObjectArgs.builder() .bucket(bucketName) .object(objectName) .filename(filePath) .build()); System.out.println("File uploaded successfully"); } catch (Exception e) { e.printStackTrace(); } } } ``` 在上面的代码中,我们通过注入的`MinioClient`对象调用`uploadObject`方法来上传文件。 这只是一个简单的示例,您可以根据您的需求进一步扩展和使用MinIO的其他功能。 请确保在`application.properties`或`application.yml`配置文件中提供正确的MinIO连接信息,例如: ``` minio.accessKey=your_access_key minio.secretKey=your_secret_key minio.endpoint=http://localhost:9000 ``` 这是一个基本的MinIOSpring Boot集成示例。您可以根据您的具体需求和业务逻辑进行进一步的开发和调整。希望对您有所帮助!如果还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值