MinIO 结合 SpringBoot

739 阅读3分钟

MinIO 结合 SpringBoot

MinIO 结合 SpringBoot 实现文件上传功能

MinIO 的基本使用

《MinIO 搭建可视化对象存储服务》 juejin.cn/post/700214…

1 使用 Docker 安装 MinIO

1.1 下载 MinIO 镜像

docker pull minio/minio

1.2 启动 MinIO 容器

docker run -p 9000:9000 -p 9001:9001 --name minio \
  -v /mydata/minio/data:/data \
  -v /mydata/minio/config:/root/.minio \
  -e "MINIO_ROOT_USER=root" \
  -e "MINIO_ROOT_PASSWORD=123456789" \
  -d minio/minio server /data \
  --console-address ':9001'

2 配置项目环境

2.1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>cn.muzaijian.mall</groupId>
    <artifactId>minio</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>minio</name>
    <description>MinIO project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <minio.version>8.0.3</minio.version>
        <hutool-all.version>5.6.7</hutool-all.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- MinIO JAVA SDK -->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>${minio.version}</version>
        </dependency>
        <!-- Hutool Java 工具包 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool-all.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Spring Boot 插件,可以把应用打包为可执行 Jar -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.2 application.yml

server:
  port: 9091
  servlet:
    multipart:
      enabled: true # 文件上传开关
      max-file-size: 5MB # 最大上传文件大小
minio:
  endpoint: http://172.17.0.3:9000 # MinIO 服务地址
  bucketName: mall # 存储桶名称
  accessKey: root # 访问的 key
  secretKey: 123456789 # 访问的 secret

3 编写文件上传代码

3.1 MinioController

package cn.muzaijian.mall.minio.controller;

import cn.muzaijian.mall.minio.domain.vo.MinioUploadVO;
import cn.muzaijian.mall.minio.manager.MinioManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

/**
 * <p>
 * Minio 对象存储 前端控制器
 * </p>
 *
 * @author muzaijian
 * @date 2021/7/23
 */
@RestController
@RequestMapping("/minio")
public class MinioController {

    @Autowired
    MinioManager minioManager;

    /**
     * 文件上传
     *
     * @param file 上传的文件
     * @return MinIO 文件上传结果 VO
     */
    @PostMapping
    public MinioUploadVO upload(@RequestParam("file") MultipartFile file) {
        return minioManager.upload(file);
    }

    /**
     * 文件删除
     *
     * @param objectName 文件所在存储桶的完整路径与名称
     * @return 文件删除结果
     */
    @DeleteMapping
    public boolean delete(@RequestParam String objectName) {
        return minioManager.delete(objectName);
    }

}

3.2 MinioManager

package cn.muzaijian.mall.minio.manager;

import cn.muzaijian.mall.minio.domain.vo.MinioUploadVO;
import org.springframework.web.multipart.MultipartFile;

/**
 * <p>
 * Minio 通用业务处理类
 * </p>
 *
 * @author muzaijian
 * @date 2021/7/28
 */
public interface MinioManager {

    /**
     * 文件上传
     *
     * @param file 上传的文件
     * @return MinIO 文件上传结果 VO
     */
    MinioUploadVO upload(MultipartFile file);

    /**
     * 文件删除
     *
     * @param objectName 文件所在存储桶的完整路径与名称
     * @return 文件删除结果
     */
    boolean delete(String objectName);
}

3.3 MinioManagerImpl

package cn.muzaijian.mall.minio.manager.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import cn.muzaijian.mall.minio.domain.dto.BucketPolicyConfigDTO;
import cn.muzaijian.mall.minio.domain.vo.MinioUploadVO;
import cn.muzaijian.mall.minio.manager.MinioManager;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.ObjectWriteArgs;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.SetBucketPolicyArgs;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

/**
 * <p>
 * Minio 通用业务处理实现类
 * </p>
 *
 * @author muzaijian
 * @date 2021/7/30
 */
@Service
@Slf4j
public class MinioManagerImpl implements MinioManager {

    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.bucketName}")
    private String bucketName;
    @Value("${minio.accessKey}")
    private String accessKey;
    @Value("${minio.secretKey}")
    private String secretKey;

    @Override
    public MinioUploadVO upload(MultipartFile file) {
        String filename = file.getOriginalFilename();
        try {
            // 创建 MinIO 的 Java 客户端
            MinioClient minioClient = MinioClient.builder()
                    .endpoint(endpoint)
                    .credentials(accessKey, secretKey)
                    .build();
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (isExist) {
                log.info("{} 存储桶已经存在。", bucketName);
            } else {
                // 创建存储桶并设置匿名用户只读权限
                MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder()
                        .bucket(bucketName)
                        .build();
                minioClient.makeBucket(makeBucketArgs);
                BucketPolicyConfigDTO bucketPolicyConfigDTO = initBucketPolicyConfigDTO(bucketName);
                SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
                        .bucket(bucketName)
                        .config(JSONUtil.toJsonStr(bucketPolicyConfigDTO))
                        .build();
                minioClient.setBucketPolicy(setBucketPolicyArgs);
            }
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
            // 设置存储对象名称
            String objectName = dtf.format(LocalDate.now()) + "/" + System.currentTimeMillis() + "_" + filename;
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .contentType(file.getContentType())
                    .stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE)
                    .build();
            minioClient.putObject(putObjectArgs);
            log.info("{} 文件上传成功。", filename);
            return MinioUploadVO.builder()
                    .name(filename)
                    .url(endpoint + "/" + bucketName + "/" + objectName)
                    .build();
        } catch (Exception e) {
            log.warn("{} 文件上传发生错误: {}", filename, e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public boolean delete(String objectName) {
        try {
            // 创建 MinIO 的 Java 客户端
            MinioClient minioClient = MinioClient.builder()
                    .endpoint(endpoint)
                    .credentials(accessKey, secretKey)
                    .build();
            RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build();
            minioClient.removeObject(removeObjectArgs);
            log.info("{} 文件删除成功。", objectName);
            return true;
        } catch (Exception e) {
            log.warn("{} 文件删除发生错误: {}", objectName, e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 根据存储桶名称初始化存储桶策略
     *
     * @param bucketName 存储桶名称
     * @return MinIO 存储桶策略 DTO
     */
    private BucketPolicyConfigDTO initBucketPolicyConfigDTO(String bucketName) {
        BucketPolicyConfigDTO.Statement statement = BucketPolicyConfigDTO.Statement.builder()
                .Effect("Allow")
                .Principal("*")
                .Action(CollUtil.toList("s3:GetObject"))
                .Resource("arn:aws:s3:::" + bucketName + "/*")
                .build();
        return BucketPolicyConfigDTO.builder()
                .Version("2012-10-17")
                .Statement(CollUtil.toList(statement))
                .build();
    }
}

3.4 MinioUploadVO

package cn.muzaijian.mall.minio.domain.vo;

import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * <p>
 * MinIO 文件上传结果 VO
 * </p>
 *
 * @author muzaijian
 * @date 2021/7/30
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Builder
public class MinioUploadVO {

    /**
     * 文件访问 url
     */
    private String url;

    /**
     * 文件名称
     */
    private String name;
}

3.5 BucketPolicyConfigDTO

package cn.muzaijian.mall.minio.domain.dto;

import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;
import java.util.List;

/**
 * <p>
 * MinIO 存储桶策略 DTO
 * </p>
 *
 * @author muzaijian
 * @date 2021/7/30
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Builder
public class BucketPolicyConfigDTO implements Serializable {
    private String Version;
    private List<Statement> Statement;

    @Data
    @EqualsAndHashCode(callSuper = false)
    @Builder
    public static class Statement {
        private String Effect;
        private String Principal;
        private List<String> Action;
        private String Resource;
    }
}

4 启动项目

4.1 验证上传文件接口

4.1.1 使用 Postman 调用接口

QQ截图20210827112321.png

4.1.2 查看返回结果

{
    "url": "172.17.0.3:9000/mall/20210827/1630032288494_aaaa.jpg",
    "name": "aaaa.jpg"
}

4.1.3 直接访问 url

QQ截图20210827112933.png

4.1.4 在 MinIO Console 查看存储结果

QQ截图20210827113201.png

4.2 验证删除文件接口

4.2.1 使用 Postman 调用接口

QQ截图20210827113640.png

4.2.2 在 MinIO Console 查看删除结果

QQ截图20210827113759.png

4.2.3 直接访问 url

QQ截图20210827113924.png

5 项目源码地址

github.com/a616766585/…