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 调用接口
4.1.2 查看返回结果
{
"url": "172.17.0.3:9000/mall/20210827/1630032288494_aaaa.jpg",
"name": "aaaa.jpg"
}