引入Maven依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.3.0</version>
</dependency>
配置文件
## ====================== ↓↓↓↓↓↓ MinIO文件服务器 ↓↓↓↓↓↓ ======================
minio:
url: http://192.168.1.222:7901/ #注意此处端口,并非Minio默认端口,并且不是Minio网页端口
accessKey: minio
secretKey: minio@123
bucket: test
配置文件实体类
/**
* @author Wang
*/
@Data
@Validated
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
/**
* 服务地址
*/
@NotEmpty(message = "minio服务地址不可为空")
@URL(message = "minio服务地址格式错误")
private String url;
/**
* 认证账户
*/
@NotEmpty(message = "minio认证账户不可为空")
private String accessKey;
/**
* 认证密码
*/
@NotEmpty(message = "minio认证密码不可为空")
private String secretKey;
/**
* 桶名称, 优先级最低
*/
private String bucket;
/**
* 桶不在的时候是否新建桶
*/
private boolean createBucket = true;
/**
* 启动的时候检查桶是否存在
*/
private boolean checkBucket = true;
/**
* 设置HTTP连接、写入和读取超时。值为0意味着没有超时
* HTTP连接超时,以毫秒为单位。
*/
private long connectTimeout;
/**
* 设置HTTP连接、写入和读取超时。值为0意味着没有超时
* HTTP写超时,以毫秒为单位。
*/
private long writeTimeout;
/**
* 设置HTTP连接、写入和读取超时。值为0意味着没有超时
* HTTP读取超时,以毫秒为单位。
*/
private long readTimeout;
}
Springboot启动类
/**
* 用户启动器
*
* @author Chill
*/
@EnableBladeFeign
@SpringCloudApplication
@ConditionalOnClass(MinioClient.class)
@Slf4j
public class ControlApplication {
public static void main(String[] args) {
BladeApplication.run(LauncherConstant.APPLICATION_CONTROL_NAME, ControlApplication.class, args);
}
@Resource
private MinioProperties minioAutoProperties;
@Bean
public MinioClient minioClient() {
log.info("开始初始化MinioClient, url为{}, accessKey为:{}", minioAutoProperties.getUrl(), minioAutoProperties.getAccessKey());
MinioClient minioClient = MinioClient
.builder()
.endpoint(minioAutoProperties.getUrl())
.credentials(minioAutoProperties.getAccessKey(), minioAutoProperties.getSecretKey())
.build();
minioClient.setTimeout(
minioAutoProperties.getConnectTimeout(),
minioAutoProperties.getWriteTimeout(),
minioAutoProperties.getReadTimeout()
);
// Start detection
if (minioAutoProperties.isCheckBucket()) {
log.info("checkBucket为{}, 开始检测桶是否存在", minioAutoProperties.isCheckBucket());
String bucketName = minioAutoProperties.getBucket();
if (!checkBucket(bucketName, minioClient)) {
log.info("文件桶[{}]不存在, 开始检查是否可以新建桶", bucketName);
if (minioAutoProperties.isCreateBucket()) {
log.info("createBucket为{},开始新建文件桶", minioAutoProperties.isCreateBucket());
createBucket(bucketName, minioClient);
}
}
log.info("文件桶[{}]已存在, minio客户端连接成功!", bucketName);
} else {
throw new RuntimeException("桶不存在, 请检查桶名称是否正确或者将checkBucket属性改为false");
}
return minioClient;
}
private boolean checkBucket(String bucketName, MinioClient minioClient) {
boolean isExists = false;
try {
isExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
throw new RuntimeException("failed to check if the bucket exists", e);
}
return isExists;
}
private void createBucket(String bucketName, MinioClient minioClient) {
try {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
log.info("文件桶[{}]新建成功, minio客户端已连接", bucketName);
} catch (Exception e) {
throw new RuntimeException("failed to create default bucket", e);
}
}
}
Minio 工具类
package org.springblade.control.config;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.IntFunction;
/**
* Minio工具类
*
* @author wh
* @version 1.0.0
*/
@Component
public class MinioUtils {
@Resource
private MinioClient minioClient;
@Resource
private MinioProperties minioProperties;
/**
* 判断桶是否存在
*
* @param bucketName bucket名称
* @return true存在,false不存在
*/
public Boolean bucketExists(String bucketName) {
try {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
throw new RuntimeException("检查桶是否存在失败!", e);
}
}
/**
* 创建bucket
*
* @param bucketName bucket名称
*/
public void createBucket(String bucketName) {
if (!this.bucketExists(bucketName)) {
try {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
throw new RuntimeException("创建桶失败!", e);
}
}
}
/**
* 上传MultipartFile文件到全局默认文件桶中
*
* @param file 文件
* @return 文件对应的URL
*/
public String putObject(MultipartFile file) {
// 给文件名添加时间戳防止重复
String fileName = getFileName(Objects.requireNonNull(file.getOriginalFilename()));
// 开始上传
this.putMultipartFile(minioProperties.getBucket(), fileName, file);
return minioProperties.getUrl() + "/" + minioProperties.getBucket() + "/" + fileName;
}
/**
* 上传文件
*
* @param objectName 文件名
* @param stream 文件流
* @param contentType 文件类型, 例如 image/jpeg: jpg图片格式, 详细可看: https://www.runoob.com/http/http-content-type.html
* @return 文件url
*/
public String putObject(String objectName, InputStream stream, String contentType) {
// 给文件名添加时间戳防止重复
String fileName = getFileName(objectName);
// 开始上传
this.putInputStream(minioProperties.getBucket(), fileName, stream, contentType);
return minioProperties.getUrl() + "/" + minioProperties.getBucket() + "/" + fileName;
}
/**
* 上传bytes文件
*
* @param objectName 文件名
* @param bytes 字节
* @param contentType 文件类型, 例如 image/jpeg: jpg图片格式, 详细可看: https://www.runoob.com/http/http-content-type.html
* @return 文件url
*/
public String putObject(String objectName, byte[] bytes, String contentType) {
// 给文件名添加时间戳防止重复
String fileName = getFileName(objectName);
// 开始上传
this.putBytes(minioProperties.getBucket(), fileName, bytes, contentType);
return minioProperties.getUrl() + "/" + minioProperties.getBucket() + "/" + fileName;
}
/**
* 上传MultipartFile文件到全局默认文件桶下的folder文件夹下
*
* @param objectName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @param file 文件
* @return 文件对应的URL
*/
public String putObject(String objectName, MultipartFile file) {
// 给文件名添加时间戳防止重复
objectName = getFileName(objectName);
// 开始上传
this.putMultipartFile(minioProperties.getBucket(), objectName, file);
return minioProperties.getUrl() + "/" + minioProperties.getBucket() + "/" + objectName;
}
/**
* 上传MultipartFile文件到指定的文件桶下
*
* @param bucketName 桶名称
* @param objectName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @param file 文件
* @return 文件对应的URL
*/
public String putObject(String bucketName, String objectName, MultipartFile file) {
// 先创建桶
this.createBucket(bucketName);
// 给文件名添加时间戳防止重复
objectName = getFileName(objectName);
// 开始上传
this.putMultipartFile(bucketName, objectName, file);
return minioProperties.getUrl() + "/" + bucketName + "/" + objectName;
}
/**
* 上传流到指定的文件桶下
*
* @param bucketName 桶名称
* @param objectName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @param stream 文件流
* @param contentType 文件类型, 例如 image/jpeg: jpg图片格式, 详细可看: https://www.runoob.com/http/http-content-type.html
* @return 文件对应的URL
*/
public String putObject(String bucketName, String objectName, InputStream stream, String contentType) {
// 先创建桶
this.createBucket(bucketName);
// 给文件名添加时间戳防止重复
String fileName = getFileName(objectName);
// 开始上传
this.putInputStream(bucketName, fileName, stream, contentType);
return minioProperties.getUrl() + "/" + bucketName + "/" + fileName;
}
/**
* 上传流到指定的文件桶下
*
* @param bucketName 桶名称
* @param objectName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @param bytes 字节
* @param contentType 文件类型, 例如 image/jpeg: jpg图片格式, 详细可看: https://www.runoob.com/http/http-content-type.html
* @return 文件对应的URL
*/
public String putObject(String bucketName, String objectName, byte[] bytes, String contentType) {
// 先创建桶
this.createBucket(bucketName);
// 给文件名添加时间戳防止重复
String fileName = getFileName(objectName);
// 开始上传
this.putBytes(bucketName, fileName, bytes, contentType);
return minioProperties.getUrl() + "/" + bucketName + "/" + fileName;
}
/**
* 上传File文件到默认桶下
*
* @param objectName 文件名
* @param file 文件
* @param contentType 文件类型, 例如 image/jpeg: jpg图片格式, 详细可看: https://www.runoob.com/http/http-content-type.html
* @return 文件对应的URL
*/
public String putObject(String objectName, File file, String contentType) {
// 给文件名添加时间戳防止重复
String fileName = getFileName(objectName);
// 开始上传
this.putFile(minioProperties.getBucket(), fileName, file, contentType);
return minioProperties.getUrl() + "/" + minioProperties.getBucket() + "/" + fileName;
}
/**
* 上传File文件
*
* @param bucketName 文件桶
* @param objectName 文件名
* @param file 文件
* @param contentType 文件类型, 例如 image/jpeg: jpg图片格式, 详细可看: https://www.runoob.com/http/http-content-type.html
* @return 文件对应的URL
*/
public String putObject(String bucketName, String objectName, File file, String contentType) {
// 先创建桶
this.createBucket(bucketName);
// 给文件名添加时间戳防止重复
String fileName = getFileName(objectName);
// 开始上传
this.putFile(bucketName, fileName, file, contentType);
return minioProperties.getUrl() + "/" + bucketName + "/" + fileName;
}
/**
* 判断文件是否存在
*
* @param objectName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @return true存在, 反之
*/
public Boolean checkFileIsExist(String objectName) {
return this.checkFileIsExist(minioProperties.getBucket(), objectName);
}
/**
* 判断文件夹是否存在
*
* @param folderName 文件夹名称
* @return true存在, 反之
*/
public Boolean checkFolderIsExist(String folderName) {
return this.checkFolderIsExist(minioProperties.getBucket(), folderName);
}
/**
* 判断文件是否存在
*
* @param bucketName 桶名称
* @param objectName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @return true存在, 反之
*/
public Boolean checkFileIsExist(String bucketName, String objectName) {
try {
minioClient.statObject(
StatObjectArgs.builder().bucket(bucketName).object(objectName).build()
);
} catch (Exception e) {
return false;
}
return true;
}
/**
* 判断文件夹是否存在
*
* @param bucketName 桶名称
* @param folderName 文件夹名称
* @return true存在, 反之
*/
public Boolean checkFolderIsExist(String bucketName, String folderName) {
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs
.builder()
.bucket(bucketName)
.prefix(folderName)
.recursive(false)
.build());
for (Result<Item> result : results) {
Item item = result.get();
if (item.isDir() && folderName.equals(item.objectName())) {
return true;
}
}
} catch (Exception e) {
return false;
}
return true;
}
/**
* 根据文件全路径获取文件流
*
* @param objectName 文件名称
* @return 文件流
*/
public InputStream getObject(String objectName) {
return this.getObject(minioProperties.getBucket(), objectName);
}
/**
* 根据文件桶和文件全路径获取文件流
*
* @param bucketName 桶名称
* @param objectName 文件名
* @return 文件流
*/
public InputStream getObject(String bucketName, String objectName) {
try {
return minioClient
.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
} catch (Exception e) {
throw new RuntimeException("根据文件名获取流失败!", e);
}
}
/**
* 根据url获取文件流
*
* @param url 文件URL
* @return 文件流
*/
public InputStream getObjectByUrl(String url) {
try {
return new URL(url).openStream();
} catch (IOException e) {
throw new RuntimeException("根据URL获取流失败!", e);
}
}
/**
* 获取全部bucket
*
* @return 所有桶信息
*/
public List<Bucket> getAllBuckets() {
try {
return minioClient.listBuckets();
} catch (Exception e) {
throw new RuntimeException("获取全部存储桶失败!", e);
}
}
/**
* 根据bucketName获取信息
*
* @param bucketName bucket名称
* @return 单个桶信息
*/
public Optional<Bucket> getBucket(String bucketName) {
try {
return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
} catch (Exception e) {
throw new RuntimeException("根据存储桶名称获取信息失败!", e);
}
}
/**
* 根据bucketName删除信息
*
* @param bucketName bucket名称
*/
public void removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
throw new RuntimeException("根据存储桶名称删除桶失败!", e);
}
}
/**
* 删除文件
*
* @param objectName 文件名称
*/
public boolean removeObject(String objectName) {
try {
this.removeObject(minioProperties.getBucket(), objectName);
} catch (Exception e) {
return false;
}
return true;
}
/**
* 删除文件
*
* @param bucketName bucket名称
* @param objectName 文件名称
*/
public boolean removeObject(String bucketName, String objectName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
} catch (Exception e) {
return false;
}
return true;
}
/**
* 上传MultipartFile通用方法
*
* @param bucketName 桶名称
* @param objectName 文件名
* @param file 文件
*/
private void putMultipartFile(String bucketName, String objectName, MultipartFile file) {
InputStream stream = null;
try {
stream = file.getInputStream();
} catch (IOException e) {
throw new RuntimeException("文件流获取错误", e);
}
try {
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.contentType(file.getContentType())
.stream(stream, stream.available(), -1)
.build()
);
} catch (Exception e) {
throw new RuntimeException("文件流上传错误", e);
}
}
/**
* 上传InputStream通用方法
*
* @param bucketName 桶名称
* @param objectName 文件名
* @param stream 文件流
*/
private void putInputStream(String bucketName, String objectName, InputStream stream, String contentType) {
try {
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.contentType(contentType)
.stream(stream, stream.available(), -1)
.build()
);
} catch (Exception e) {
throw new RuntimeException("文件流上传错误", e);
}
}
/**
* 上传 bytes 通用方法
*
* @param bucketName 桶名称
* @param objectName 文件名
* @param bytes 字节编码
*/
private void putBytes(String bucketName, String objectName, byte[] bytes, String contentType) {
// 字节转文件流
InputStream stream = new ByteArrayInputStream(bytes);
try {
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.contentType(contentType)
.stream(stream, stream.available(), -1)
.build()
);
} catch (Exception e) {
throw new RuntimeException("文件流上传错误", e);
}
}
/**
* 上传 file 通用方法
*
* @param bucketName 桶名称
* @param objectName 文件名
* @param file 文件
* @param contentType 文件类型, 例如 image/jpeg: jpg图片格式, 详细可看: https://www.runoob.com/http/http-content-type.html
*/
private void putFile(String bucketName, String objectName, File file, String contentType) {
try {
FileInputStream fileInputStream = new FileInputStream(file);
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.contentType(contentType)
.stream(fileInputStream, fileInputStream.available(), -1)
.build()
);
} catch (Exception e) {
throw new RuntimeException("文件上传错误", e);
}
}
/**
* 生成唯一ID
*
* @param objectName 文件名
* @return 唯一ID
*/
private static String getFileName(String objectName) {
//判断文件最后一个点所在的位置
int lastIndexOf = objectName.lastIndexOf(".");
if (lastIndexOf == -1) {
return String.format("%s_%s", objectName, System.currentTimeMillis());
} else {
// 获取文件前缀,已最后一个点进行分割
String filePrefix = objectName.substring(0, objectName.lastIndexOf("."));
// 获取文件后缀,已最后一个点进行分割
String fileSuffix = objectName.substring(objectName.lastIndexOf(".") + 1);
// 组成唯一文件名
return String.format("%s_%s.%s", filePrefix, System.currentTimeMillis(), fileSuffix);
}
}
/**
* 获取文件信息, 如果抛出异常则说明文件不存在
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
*/
public StatObjectResponse getObjectInfo(String bucketName, String objectName) throws Exception {
return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
}
/**
* 获取文件外链
*
* @param bucketName bucket名称
* @param objectName 文件名称
* @param expires 过期时间 <=7
* @return url
*/
@SneakyThrows
public String getObjectURL(String bucketName, String objectName, Integer expires) {
IntFunction<Integer> integerIntFunction = (int i) -> {
return Math.min(i, 7);
};
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.bucket(bucketName)
.object(objectName)
.expiry(integerIntFunction.apply(expires))
.build());
}
/**
* 根据文件前置查询文件
*
* @param bucketName bucket名称
* @param prefix 前缀
* @param recursive 是否递归查询
* @return MinioItem 列表
*/
@SneakyThrows
public List<Item> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
List<Item> list = new ArrayList<>();
Iterable<Result<Item>> objectsIterator = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).prefix(prefix)
.recursive(recursive).build()
);
if (objectsIterator != null) {
for (Result<Item> result : objectsIterator) {
Item item = result.get();
list.add(item);
}
}
return list;
}
}