SpringBoot 2.3.2.RELEASE + Minio 8.2.1
MinIO 是在 Apache License v2.0 下发布的高性能对象存储。 它是与 Amazon S3 云存储服务兼容的 API。 使用 MinIO 构建 用于机器学习、分析和应用的高性能基础设施 数据工作负载。
什么是对象存储?
- An object 是二进制数据,有时也称为 Binary 大对象 (BLOB)。 Blob 可以是图像、音频文件、电子表格,甚至 二进制可执行代码。 像 MinIO 这样的对象存储平台提供了专用的 用于存储、检索和搜索 blob 的工具和功能。
- MinIO 对象存储使用 buckets 来组织对象。 存储桶类似于文件系统中的文件夹或目录,其中每个 桶可以容纳任意数量的对象。 MinIO 存储桶提供 与 AWS S3 存储桶相同的功能。
1.引入依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
</dependency>
2.配置applicaton.yml文件
minio:
endPoint: http://ip地址:端口
accessKey: 账号
secretKey: 密码
bucketName: 存储的桶名称

3.配置文件—MinioProperties
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
private String endPoint;
private String accessKey;
private String secretKey;
private String bucketName;
}
import io.minio.MinioClient;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration
@EnableConfigurationProperties(MinioProperties.class)
public class MinIOConfig {
@Resource
private MinioProperties minioProperties;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(minioProperties.getEndPoint())
.credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
.build();
}
}
4.工具类—MinioUtils
import cn.hutool.core.codec.Base64;
import com.google.common.io.ByteStreams;
import com.space.wedding.enums.Base64PrefixEnum;
import io.minio.*;
import io.minio.http.Method;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.util.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
@Component
public class MinioUtils {
@Resource
private MinioClient minioClient;
public void createBucket(String bucketName) throws Exception {
boolean flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
System.out.println(flag);
if (!flag) {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
}
}
public String uploadFile(MultipartFile file, String bucketName, String filePrefix, String fileName) throws Exception {
if (null == file || 0 == file.getSize()) {
return null;
}
createBucket(bucketName);
String objectName = filePrefix + fileName + Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf("."));
PutObjectArgs build = PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build();
return minioClient.putObject(build).object();
}
public String reuploadFile(MultipartFile file, String bucketName, String objectName) throws Exception {
if (null == file || 0 == file.getSize()) {
return null;
}
PutObjectArgs build = PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build();
return minioClient.putObject(build).object();
}
public void putObject(String bucketName, String objectName, InputStream stream) throws Exception {
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(stream, stream.available(), -1)
.contentType(objectName.substring(objectName.lastIndexOf(".")))
.build());
}
public void putObject(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(stream, size, -1)
.contentType(contextType)
.build());
}
public InputStream getObject(String bucketName, String objectName) throws Exception {
return minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
}
public void getObjectInfo(String bucketName, String objectName, HttpServletResponse response) throws Exception {
StatObjectResponse statObjectResponse = minioClient.statObject(StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
byte[] buffer = new byte[1024];
int length = (int) statObjectResponse.size();
try (InputStream inputStream = getObject(bucketName, objectName);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(length)) {
ByteStreams.copy(inputStream, outputStream);
buffer = outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition",
"attachment; filename=" + new String(objectName.getBytes(StandardCharsets.UTF_8), "ISO8859-1"));
response.getOutputStream().write(buffer);
response.flushBuffer();
response.getOutputStream().close();
}
public String getBase64(String bucketName, String objectName) {
String suffix = objectName.substring(objectName.indexOf("."));
String prefixByType = Base64PrefixEnum.getPrefixByType(suffix);
try {
InputStream object = getObject(bucketName, objectName);
byte[] bytes = IOUtils.toByteArray(object);
return prefixByType + Base64.encode(bytes);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String getPreviewUrl(String bucketName, String fileName) {
if (StringUtils.isNotBlank(fileName)) {
try {
minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(fileName)
.build());
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
public void removeObject(String bucketName, String objectName) throws Exception {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
}
}
5.使用案例—多文件上传(可调整)、删除
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.space.wedding.config.minio.MinioProperties;
import com.space.wedding.dao.ApplicationResourceDao;
import com.space.wedding.entity.ApplicationResource;
import com.space.wedding.exception.MyCustomException;
import com.space.wedding.response.StatusCodeEnum;
import com.space.wedding.service.ApplicationResourceService;
import com.space.wedding.utils.MinioUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class ApplicationResourceServiceImpl extends ServiceImpl<ApplicationResourceDao, ApplicationResource> implements ApplicationResourceService {
@Resource
private MinioUtils minioUtils;
@Resource
private MinioProperties minioProperties;
@Override
public void insertResource(MultipartFile[] fileList, String resourceId, String dir, int type) {
String filePrefix = type == 1 ? "IMG" : (type == 2 ? "VIDEO" : "DOC");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
if (fileList == null || fileList.length == 0) {
return;
}
deleteResource(resourceId, type);
try {
List<String> fileUrlList = new ArrayList<>();
for (MultipartFile file : fileList) {
String finalFileName = filePrefix + dateFormat.format(new Date());
String uploadFileUrl = minioUtils.uploadFile(file, minioProperties.getBucketName(), dir, finalFileName);
fileUrlList.add(uploadFileUrl);
}
List<ApplicationResource> insertList = fileUrlList.stream().map(item -> {
ApplicationResource resource = new ApplicationResource();
resource.setResourceId(resourceId);
resource.setResourceType(type);
resource.setResourceUrl(item);
return resource;
}).collect(Collectors.toList());
if (!insertList.isEmpty()) {
this.saveBatch(insertList);
}
} catch (Exception e) {
throw new MyCustomException(StatusCodeEnum.UPLOAD_FILE_EXCEPTION);
}
}
@Override
public void deleteResource(String resourceId, int type) {
try {
LambdaQueryWrapper<ApplicationResource> queryWrapper = new LambdaQueryWrapper<ApplicationResource>()
.eq(ApplicationResource::getResourceId, resourceId);
if (type > 0) {
queryWrapper.eq(ApplicationResource::getResourceType, type);
}
List<ApplicationResource> list = baseMapper.selectList(queryWrapper);
List<String> collect = list.stream().map(ApplicationResource::getResourceUrl).collect(Collectors.toList());
for (String url : collect) {
minioUtils.removeObject(minioProperties.getBucketName(), url);
}
} catch (Exception e) {
throw new MyCustomException(StatusCodeEnum.DELETE_FILE_EXCEPTION);
}
}
}
5.Minio服务器搭建
Docker - 安装Minio