1 前言
之前介绍了如何使用Minio提供的JAVA SDK进行上传和下载文件,在此基础上,我们可以使用spring boot集成Minio JAVA SDK,添加自动配置、装配、客户端管理等功能,简化开发
2 Spring Boot集成Minio
2.1 环境搭建
首先我们搭建一个spring boot基础工程,引入以下依赖
xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/io.minio/minio -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.3.1</version>
</dependency>
<dependency>
<groupId>me.tongfei</groupId>
<artifactId>progressbar</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.2</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.13</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
2.2 操作模板类
在spring中,提供了很多集成第三方的操作模板类,比如RedisTemplate、RestTemplate等等,我们可以参照这些,提供一个minio SDK的集成模板类,这样在使用API时就比较方便了。
首先需要创建一个OSS文件对象,上传文件成功后,我们需要将文件信息返回给前端
java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
* @author wuKeFan
* @date 2020/9/10
*/
@RefreshScope
@Configuration
public class OssConfig {
@Value("${biz.oss.endpoint}")
private String endpoint;
@Value("${biz.oss.bucket}")
private String bucket;
@Value("${biz.oss.access-key-id}")
private String accessKeyId;
@Value("${biz.oss.access-key-secret}")
private String accessKeySecret;
@Value("${biz.oss.type}")
private Integer ossType;
/**
* 最大上传长度单位m,默认20M
*/
@Value("${biz.oss.maxLength:20}")
private Integer maxLength;
public String getAccessId() {
return accessKeyId;
}
public String getBucket() {
return bucket;
}
public String getEndpoint() {
return endpoint;
}
public Integer getMaxLength() {
return maxLength;
}
public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
public String getAccessKeyId() {
return accessKeyId;
}
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public String getAccessKeySecret() {
return accessKeySecret;
}
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
public void setMaxLength(Integer maxLength) {
this.maxLength = maxLength;
}
public Integer getOssType() {
return ossType;
}
public void setOssType(Integer ossType) {
this.ossType = ossType;
}
}
java
import com.mall4j.cloud.common.exception.Mall4cloudException;
import com.mall4j.cloud.common.response.ResponseEnum;
import io.minio.*;
import io.minio.http.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @author wuKeFan
*/
@Component
public class MinioTemplate implements InitializingBean {
@Autowired
private OssConfig ossConfig;
private MinioClient minioClient;
static final Logger logger = LoggerFactory.getLogger(MinioTemplate.class);
@Override
public void afterPropertiesSet() {
this.minioClient = MinioClient.builder().endpoint(ossConfig.getEndpoint())
.credentials(ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret())
.build();
}
/**
* 删除文件
*
* @param objectName 文件名称
* @throws Exception 参考https://docs.minio.io/cn/java-client-api-reference.html#removeObject
*/
public void removeObject(String objectName) throws Exception {
minioClient.removeObject(RemoveObjectArgs.builder().object(objectName).bucket(ossConfig.getBucket()).build());
}
/**
* 获得上传的URL
* @param objectName 文件路径(对象名)
*/
public String getPresignedObjectUrl(String objectName){
try {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(ossConfig.getBucket()).object(objectName).expiry(10, TimeUnit.MINUTES).method(Method.PUT).build());
} catch (Exception e) {
e.printStackTrace();
throw new Mall4cloudException(ResponseEnum.EXCEPTION);
}
}
public void uploadMinio(byte[] bytes, String filePath, String contentType) throws IOException {
InputStream input = null;
try {
input = new ByteArrayInputStream(bytes);
minioClient.putObject(
PutObjectArgs.builder()
.bucket(ossConfig.getBucket())
.contentType(contentType)
.stream(input, input.available(), -1)
.object(filePath)
.build()
);
} catch (Exception e) {
logger.error("minio上传文件错误:", e);
} finally {
if (Objects.nonNull(input)) {
input.close();
}
}
}
}
2.3 自动配置
在了解了BAT公司提供的对象存储OSS后,发现其API接口标准都是差不多的,从扩展性的角度出发,我们当前服务应当支持各种类型的OSS,比如阿里等。所以这里先定义一个枚举类,提供除了Minio还适配其他厂商的支持。
java
/**
* 文件上传存储类型
* @author wuKeFan
* @date 2021/01/20
*/
public enum OssType {
/**
* 阿里云oss
*/
ALI(0),
/**
* minio
*/
MINIO(1),
;
private final Integer value;
public Integer value() {
return value;
}
OssType(Integer value) {
this.value = value;
}
}
3 测试
首先,在yml中添加Minio的配置:
yaml
biz:
oss:
# resources-url是带有bucket的
resources-url: http://127.0.0.1:9000/mall4cloud
# 文件上传类型 0.阿里云 1.minio
type: 1
endpoint: http://127.0.0.1:9000
bucket: mall4cloud
access-key-id: username
access-key-secret: password
然后创建一个访问接口,直接调用minioTemplate进行文件操作,这样就十分便利,达到了简化开发的目的
java
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.mall4j.cloud.biz.config.MinioTemplate;
import com.mall4j.cloud.biz.config.OssConfig;
import com.mall4j.cloud.biz.constant.OssType;
import com.mall4j.cloud.biz.vo.OssVO;
import com.mall4j.cloud.common.response.ServerResponseEntity;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* @author wuKeFan
* @date 2020/9/10
*/
@RequestMapping(value = "/oss")
@RestController
@Tag(name = "文件管理")
public class OssController {
/**
* 上传的文件夹(根据时间确定)
*/
public static final String NORM_DAY_PATTERN = "yyyy/MM/dd";
@Autowired
private OssConfig ossConfig;
@Autowired
private MinioTemplate minioTemplate;
@GetMapping(value = "/info")
@Operation(summary = "token" , description = "获取文件上传需要的token")
@Parameter(name = "fileNum", description = "需要获取token的文件数量")
public ServerResponseEntity<OssVO> info(@RequestParam("fileNum") Integer fileNum) {
OssVO ossVO = new OssVO();
// minio文件上传
if (Objects.equals(ossConfig.getOssType(), OssType.MINIO.value())) {
fillMinIoInfo(ossVO, fileNum);
}
return ServerResponseEntity.success(ossVO);
}
private void fillMinIoInfo(OssVO ossVo, Integer fileNum) {
List<OssVO> ossVOList = new ArrayList<>();
for (int i = 0; i<fileNum; i++) {
OssVO oss = loadOssVO(new OssVO());
String actionUrl = minioTemplate.getPresignedObjectUrl(oss.getDir() + oss.getFileName());
oss.setActionUrl(actionUrl);
ossVOList.add(oss);
}
ossVo.setOssList(ossVOList);
}
private OssVO loadOssVO(OssVO ossVo) {
String dir = DateUtil.format(new Date(), NORM_DAY_PATTERN)+ "/";
String fileName = IdUtil.simpleUUID();
ossVo.setDir(dir);
ossVo.setFileName(fileName);
return ossVo;
}
@PostMapping("/upload_minio")
@Operation(summary = "文件上传接口" , description = "上传文件,返回文件路径与域名")
public ServerResponseEntity<OssVO> uploadFile(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return ServerResponseEntity.success();
}
OssVO oss = loadOssVO(new OssVO());
minioTemplate.uploadMinio(file.getBytes(), oss.getDir() + oss.getFileName(), file.getContentType());
return ServerResponseEntity.success(oss);
}
}