SpringBoot 整合minio(实现minio-starter)
1)依赖导入
<dependencies>
<!-- 工具类相关 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- Spring 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- 三方云服务相关 -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.7</version>
</dependency>
</dependencies>
2)配置编写
MinioAutoConfiguration
/**
* minio自动配置类
*
* @author baiyan
*/
@Configuration
@EnableConfigurationProperties(MinioConfig.class)
public class MinioAutoConfiguration {
@Bean
public MinioClient minioClient(MinioConfig minioConfig) {
return MinioClient.builder().endpoint(minioConfig.getEndpoint(), minioConfig.getPort(), minioConfig.getSecure())
.credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey()).build();
}
@Bean
public MinioUtil minioUtil(MinioClient minioClient, MinioConfig minioConfig) {
return new MinioUtil(minioClient, minioConfig);
}
}
MinioConfig
@Data
@ConfigurationProperties(prefix = "spring.minio")
public class MinioConfig {
/**
* ip:minio地址,分布式节点情况下推荐配置一个nginx路由,转接给nginx的负载均衡
*/
private String endpoint;
/**
* 端口:minio地址,分布式节点情况推荐配置一个nginx路由,转接给nginx的负载均衡
*/
private int port;
/**
* 账号
*/
private String accessKey;
/**
* 秘钥
*/
private String secretKey;
/**
* 如果是true,则用的是https而不是http,默认值是false
*/
private Boolean secure = false;
/**
* 桶名称,默认为baiyan
*/
private String bucketName = "baiyan";
/**
* 是否开启nginx路由,与nginxLoadUrl对应
*/
private Boolean nginxLoadUrlEnable = false;
/**
* 预览的url在nginx中的前缀,minio中生成的文件预览或者下载的url是直接展示成ip:端口形式的,这个是不安全的,需要在nginx中做一层路由。保证安全性,默认不开启。
*/
private String nginxLoadUrl = "api/9c16ff1ecec";
}
3)工具类实现
/**
* minio工具类
*
* @author zxl
*/
@Slf4j
@RequiredArgsConstructor
public class MinioUtil {
/**
* 默认url过期时间
*/
public static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;
/**
* 默认最大文件上传为500M
*/
public static final int MAX_UPLOAD_FILE_SIZE = 1024 * 1024 * 500;
/**
* @description Java Attribute variables
*/
private final MinioClient minioClient;
/**
* @description Java Attribute variables
*/
private final MinioConfig minioConfig;
/**
* @return string
* @description Java Attribute variables 获取minio绝对路径
* @param objectName
*/
public String getAbsolutePath(String objectName) {
return minioConfig.getEndpoint() + (minioConfig.getNginxLoadUrlEnable() ? minioConfig.getNginxLoadUrl() : "")
+ ":" + minioConfig.getPort() + "/" + minioConfig.getBucketName() + "/" + objectName;
}
/**
* 检查存储桶是否存在
*
* @param bucketName 存储桶名称
* @return boolean
*/
@SneakyThrows
public boolean bucketExists(String bucketName) {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}
/**
* 创建存储桶
*
* @param bucketName 存储桶名称
*/
@SneakyThrows
public void makeBucket(String bucketName) {
if (!bucketExists(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}
/**
* 列出所有存储桶
*
* @return list of buckets
*/
@SneakyThrows
public List<Bucket> listBuckets() {
return minioClient.listBuckets();
}
/**
* 列出所有存储桶名称
*
* @return list
*/
@SneakyThrows
public List<String> listBucketNames() {
List<Bucket> bucketList = listBuckets();
return CollUtil.isNotEmpty(bucketList) ? bucketList.stream().map(Bucket::name).collect(Collectors.toList())
: new ArrayList<>();
}
/**
* 删除存储桶
*
* @param bucketName 存储桶名称
* @return boolean
*/
@SneakyThrows
public boolean removeBucket(String bucketName) {
boolean flag = bucketExists(bucketName);
if (flag) {
Iterable<Result<Item>> myObjects = listObjects(bucketName);
for (Result<Item> result : myObjects) {
Item item = result.get();
// 有对象文件,则删除失败
if (item.size() > 0) {
return false;
}
}
// 删除存储桶,注意,只有存储桶为空时才能删除成功。
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
flag = bucketExists(bucketName);
if (!flag) {
return true;
}
}
return false;
}
/**
* 列出存储桶中的所有对象名称
*
* @param bucketName 存储桶名称
* @return list of
*/
@SneakyThrows
public List<String> listObjectNames(String bucketName) {
List<String> listObjectNames = new ArrayList<>();
boolean flag = bucketExists(bucketName);
if (flag) {
Iterable<Result<Item>> myObjects = listObjects(bucketName);
for (Result<Item> result : myObjects) {
Item item = result.get();
listObjectNames.add(item.objectName());
}
}
return listObjectNames;
}
/**
* 列出存储桶中的所有对象
*
* @param bucketName 存储桶名称
* @return item name
*/
@SneakyThrows
public Iterable<Result<Item>> listObjects(String bucketName) {
boolean flag = bucketExists(bucketName);
if (flag) {
return minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());
}
return null;
}
/**
* 获取文件md5
*
* @param stream
* @return string
*/
public String getFileMd5(InputStream stream) {
return MD5.create().digestHex(stream);
}
/**
* 获取文件md5
*
* @param multipartFile
* @return str
*/
@SneakyThrows
public String getFileMd5(MultipartFile multipartFile) {
return this.getFileMd5(multipartFile.getInputStream());
}
/**
* 图片上传
*
* @param bucketName
* @param multipartFile
* @return string
*/
@SneakyThrows
public String putObject(String bucketName, MultipartFile multipartFile) {
Assert.isTrue(multipartFile.getSize() <= MAX_UPLOAD_FILE_SIZE, "minio.upload.file.is.too.big");
Assert.isTrue(bucketExists(bucketName), "minio.bucket.is.not.exist");
// 获取当前日期
LocalDateTime currentDate = LocalDateTime.now();
// 创建一个日期格式化对象,指定格式为"yyyyMMdd"
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
// 将当前日期格式化为指定格式的字符串
String currentTime = currentDate.format(formatter);
String objectName = UUID.randomUUID().toString() + '_' + currentTime;
log.info("minio的objectName为:{}", objectName);
this.putObject(bucketName, multipartFile.getInputStream(), objectName, multipartFile.getContentType());
return objectName;
}
/**
* 通过InputStream上传对象,远端文件中心中存储的的文件名为上传流文件的md5值,保证远端存储的文件唯一性,业务端使用的使用可以根据md5进行文件的预览url获取或者流获取。
*
* @param bucketName 存储桶名称
* @param path
* @param multipartFile
* @return string
*/
@SneakyThrows
public String putObjectWithPath(String bucketName, MultipartFile multipartFile, String path) {
Assert.isTrue(multipartFile.getSize() <= MAX_UPLOAD_FILE_SIZE, "minio.upload.file.is.too.big");
Assert.isTrue(bucketExists(bucketName), "minio.bucket.is.not.exist");
// 获取当前日期
LocalDateTime currentDate = LocalDateTime.now();
// 创建一个日期格式化对象,指定格式为"yyyyMMdd"
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
// 将当前日期格式化为指定格式的字符串
String currentTime = currentDate.format(formatter);
String objectName = UUID.randomUUID().toString() + '_' + currentTime;
log.info("minio的objectName为:{}", objectName);
this.putObject(bucketName, multipartFile.getInputStream(), objectName, multipartFile.getContentType());
return objectName;
}
/**
* 通过InputStream上传对象,远端文件中心中存储的的文件名为上传流文件的md5值,保证远端存储的文件唯一性,业务端使用的使用可以根据md5进行文件的预览url获取或者流获取。
*
* @param bucketName 存储桶名称
* @param stream 要上传的流
* @param objectName minio中文件名:取MD5
* @param contentType 文件类型
* @return string
*/
@SneakyThrows
public String putObject(String bucketName, InputStream stream, String objectName, String contentType) {
Assert.isTrue(bucketExists(bucketName), "minio.bucket.is.not.exist");
Assert.isTrue(StrUtil.isNotBlank(objectName), "minio.objectName.is.not.exist");
ObjectWriteResponse objectWriteResponse = minioClient.putObject(PutObjectArgs.builder().bucket(bucketName)
.object(objectName).contentType(contentType).stream(stream, stream.available(), -1).build());
return objectWriteResponse.object();
}
/**
* description: 上传视频
*
* @param multipartFile
* @param bucketName
* @return string
*/
public List<String> uploadVideo(String bucketName, MultipartFile[] multipartFile) {
List<String> names = new ArrayList<>(multipartFile.length);
for (MultipartFile file : multipartFile) {
String fileName = file.getOriginalFilename();
String[] split = fileName.split("\\.");
if (split.length > 1) {
fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
} else {
fileName = fileName + System.currentTimeMillis();
}
InputStream in = null;
System.err.println("文件名是:" + fileName);
try {
in = file.getInputStream();
File videoFile = File.createTempFile(split[0], "." + split[1]);
FileUtils.copyInputStreamToFile(in, videoFile);
System.err.println("路径" + videoFile.getAbsolutePath());
File output = new File(videoFile.getAbsolutePath());
InputStream inputStream = Files.newInputStream(output.toPath());
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName)
.stream(inputStream, inputStream.available(), -1).contentType(file.getContentType()).build());
inputStream.close();
output.delete();
videoFile.delete();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
names.add(fileName);
}
return names;
}
/**
* 以流的形式获取一个文件对象
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @return stream object
*/
@SneakyThrows
public InputStream getObject(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
if (flag) {
StatObjectResponse statObject = statObject(bucketName, objectName);
if (statObject != null && statObject.size() > 0) {
return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
}
}
return null;
}
/**
* 以流的形式获取一个文件对象(断点下载)
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @param offset 起始字节的位置
* @param length 要读取的长度 (可选,如果无值则代表读到文件结尾)
* @return input
*/
@SneakyThrows
public InputStream getObject(String bucketName, String objectName, long offset, Long length) {
boolean flag = bucketExists(bucketName);
if (flag) {
StatObjectResponse statObject = statObject(bucketName, objectName);
if (statObject != null && statObject.size() > 0) {
return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName)
.offset(offset).length(length).build());
}
}
return null;
}
/**
* 删除一个对象
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @return boolean
*/
@SneakyThrows
public boolean removeObject(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
if (flag) {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
return true;
}
return false;
}
/**
* 删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表
*
* @param bucketName 存储桶名称
* @param objectNames 含有要删除的多个object名称的迭代器对象
* @return list of objects
*/
@SneakyThrows
public List<String> removeObject(String bucketName, List<String> objectNames) {
Assert.isTrue(CollUtil.isNotEmpty(objectNames), "minio.delete.object.name.can.not.empty");
List<String> deleteErrorNames = new ArrayList<>();
boolean flag = bucketExists(bucketName);
if (flag) {
List<DeleteObject> objects = objectNames.stream().map(DeleteObject::new).collect(Collectors.toList());
Iterable<Result<DeleteError>> results =
minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
deleteErrorNames.add(error.objectName());
}
}
return deleteErrorNames;
}
/**
* 生成一个给HTTP PUT请求用的presigned URL。 浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @param expires 失效时间(以秒为单位),默认是7天,不得大于七天
* @return string
*/
@SneakyThrows
public String preSignedPutObject(String bucketName, String objectName, Integer expires) {
boolean flag = bucketExists(bucketName);
String url = "";
if (flag) {
url = minioClient
.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.PUT).bucket(bucketName)
.object(objectName).expiry(Objects.isNull(expires) ? DEFAULT_EXPIRY_TIME : expires).build());
}
return url;
}
/**
* 获取对象的元数据
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @return stat
*/
@SneakyThrows
public StatObjectResponse statObject(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
if (flag) {
return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
}
return null;
}
/**
* 获取URL地址
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @return string
*/
@SneakyThrows
public String getObjectUrl(String bucketName, String objectName) {
boolean flag = bucketExists(bucketName);
String url = "";
if (flag) {
url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET)
.bucket(bucketName).object(objectName).expiry(DEFAULT_EXPIRY_TIME).build());
}
return url;
}
}
自动注入
注意:编写imports文件,实现springboot的主动注入功能
--- #minio 文件服务器配置
spring:
minio:
endpoint: ${MINIO_ENDPOINT:http://192.168.3.6}
port: ${MINIO_PORT:6900}
access-key: ${MINIO_ACCESS_KEY:admin}
secret-key: ${MINIO_SECRET_KEY:password}
bucket-name: ${MINIO_BUCKET_NAME:test}
标签:SpringBoot,return,String,objectName,param,bucketName,public,starter,minio
From: https://www.cnblogs.com/zxlyy/p/18685863