Files
smartDriveEE/src/main/java/com/rj/utils/MinIOUtil.java
2025-10-06 12:17:34 +08:00

565 lines
17 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.rj.utils;
import com.rj.config.MinIOConfig;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* MinIO 工具类
* 提供文件上传、下载、删除等常用操作
*
* @author rj
* @date 2025-10-02
*/
@Slf4j
@Component
public class MinIOUtil {
@Autowired
private MinioClient minioClient;
@Autowired
private MinIOConfig minioConfig;
/**
* 检查存储桶是否存在
*
* @param bucketName 存储桶名称
* @return 是否存在
*/
public boolean bucketExists(String bucketName) {
try {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("检查存储桶是否存在失败: {}", e.getMessage());
return false;
}
}
/**
* 创建存储桶
*
* @param bucketName 存储桶名称
* @return 是否创建成功
*/
public boolean createBucket(String bucketName) {
try {
if (!bucketExists(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
log.info("存储桶创建成功: {}", bucketName);
return true;
}
log.info("存储桶已存在: {}", bucketName);
return true;
} catch (Exception e) {
log.error("创建存储桶失败: {}", e.getMessage());
return false;
}
}
/**
* 获取所有存储桶
*
* @return 存储桶列表
*/
public List<String> getAllBuckets() {
try {
List<Bucket> buckets = minioClient.listBuckets();
List<String> bucketNames = new ArrayList<>();
for (Bucket bucket : buckets) {
bucketNames.add(bucket.name());
}
return bucketNames;
} catch (Exception e) {
log.error("获取存储桶列表失败: {}", e.getMessage());
return new ArrayList<>();
}
}
/**
* 删除存储桶
*
* @param bucketName 存储桶名称
* @return 是否删除成功
*/
public boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
log.info("存储桶删除成功: {}", bucketName);
return true;
} catch (Exception e) {
log.error("删除存储桶失败: {}", e.getMessage());
return false;
}
}
/**
* 上传文件
*
* @param file 文件
* @param bucketName 存储桶名称
* @return 文件名
*/
public String uploadFile(MultipartFile file, String bucketName) {
return uploadFile(file, bucketName, null);
}
/**
* 上传文件
*
* @param file 文件
* @param bucketName 存储桶名称
* @param objectName 对象名称文件名如果为null则自动生成
* @return 文件名
*/
public String uploadFile(MultipartFile file, String bucketName, String objectName) {
try {
// 确保存储桶存在
createBucket(bucketName);
// 生成文件名
if (objectName == null || objectName.trim().isEmpty()) {
objectName = generateFileName(file.getOriginalFilename());
}
// 上传文件
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build()
);
log.info("文件上传成功: {}/{}", bucketName, objectName);
return objectName;
} catch (Exception e) {
log.error("文件上传失败: {}", e.getMessage());
throw new RuntimeException("文件上传失败: " + e.getMessage());
}
}
/**
* 上传文件流
*
* @param inputStream 文件流
* @param bucketName 存储桶名称
* @param objectName 对象名称
* @param contentType 内容类型
* @return 是否上传成功
*/
public boolean uploadFile(InputStream inputStream, String bucketName, String objectName, String contentType) {
try {
createBucket(bucketName);
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(inputStream, -1, 10485760) // 10MB
.contentType(contentType)
.build()
);
log.info("文件流上传成功: {}/{}", bucketName, objectName);
return true;
} catch (Exception e) {
log.error("文件流上传失败: {}", e.getMessage());
return false;
}
}
/**
* 上传字节数组
*
* @param data 字节数组
* @param objectName 对象名称
* @param contentType 内容类型
* @return 对象名称
*/
public String uploadFile(byte[] data, String objectName, String contentType) {
try {
// 使用配置中的默认存储桶
String bucketName = minioConfig.getBucketName();
// 确保存储桶存在
createBucket(bucketName);
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(new java.io.ByteArrayInputStream(data), data.length, -1)
.contentType(contentType)
.build()
);
log.info("字节数组上传成功: {}/{}, 大小: {} bytes", bucketName, objectName, data.length);
return objectName;
} catch (Exception e) {
log.error("字节数组上传失败: {}", e.getMessage());
throw new RuntimeException("字节数组上传失败: " + e.getMessage());
}
}
/**
* 下载文件
*
* @param bucketName 存储桶名称
* @param objectName 对象名称
* @return 文件流
*/
public InputStream downloadFile(String bucketName, String objectName) {
try {
return minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build()
);
} catch (Exception e) {
log.error("文件下载失败: {}", e.getMessage());
throw new RuntimeException("文件下载失败: " + e.getMessage());
}
}
/**
* 下载文件到本地
*
* @param bucketName 存储桶名称
* @param objectName 对象名称
* @param fileName 本地文件路径
* @return 是否下载成功
*/
public boolean downloadFile(String bucketName, String objectName, String fileName) {
try {
minioClient.downloadObject(
DownloadObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.filename(fileName)
.build()
);
log.info("文件下载成功: {}/{} -> {}", bucketName, objectName, fileName);
return true;
} catch (Exception e) {
log.error("文件下载失败: {}", e.getMessage());
return false;
}
}
/**
* 删除文件
*
* @param bucketName 存储桶名称
* @param objectName 对象名称
* @return 是否删除成功
*/
public boolean removeFile(String bucketName, String objectName) {
try {
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build()
);
log.info("文件删除成功: {}/{}", bucketName, objectName);
return true;
} catch (Exception e) {
log.error("文件删除失败: {}", e.getMessage());
return false;
}
}
/**
* 获取文件信息
*
* @param bucketName 存储桶名称
* @param objectName 对象名称
* @return 文件信息
*/
public StatObjectResponse getFileInfo(String bucketName, String objectName) {
try {
return minioClient.statObject(
StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build()
);
} catch (Exception e) {
log.error("获取文件信息失败: {}", e.getMessage());
return null;
}
}
/**
* 获取文件列表
*
* @param bucketName 存储桶名称
* @return 文件列表
*/
public List<String> listFiles(String bucketName) {
return listFiles(bucketName, null);
}
/**
* 获取文件列表
*
* @param bucketName 存储桶名称
* @param prefix 文件前缀
* @return 文件列表
*/
public List<String> listFiles(String bucketName, String prefix) {
List<String> files = new ArrayList<>();
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(bucketName)
.prefix(prefix)
.build()
);
for (Result<Item> result : results) {
Item item = result.get();
files.add(item.objectName());
}
} catch (Exception e) {
log.error("获取文件列表失败: {}", e.getMessage());
}
return files;
}
/**
* 获取文件详细列表
*
* @param bucketName 存储桶名称
* @return 文件详细信息列表
*/
public List<FileInfo> listFileDetails(String bucketName) {
return listFileDetails(bucketName, null);
}
/**
* 获取文件详细列表
*
* @param bucketName 存储桶名称
* @param prefix 文件前缀
* @return 文件详细信息列表
*/
public List<FileInfo> listFileDetails(String bucketName, String prefix) {
List<FileInfo> files = new ArrayList<>();
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(bucketName)
.prefix(prefix)
.build()
);
for (Result<Item> result : results) {
Item item = result.get();
FileInfo fileInfo = new FileInfo();
fileInfo.setObjectName(item.objectName());
fileInfo.setSize(item.size());
fileInfo.setLastModified(item.lastModified());
fileInfo.setEtag(item.etag());
files.add(fileInfo);
}
} catch (Exception e) {
log.error("获取文件详细列表失败: {}", e.getMessage());
}
return files;
}
/**
* 获取文件预签名URL用于临时访问
*
* @param bucketName 存储桶名称
* @param objectName 对象名称
* @param expires 过期时间(秒)
* @return 预签名URL
*/
public String getPresignedObjectUrl(String bucketName, String objectName, int expires) {
try {
// 检查文件是否存在
if (!objectExists(bucketName, objectName)) {
log.error("文件不存在: bucket={}, object={}", bucketName, objectName);
return null;
}
log.info("生成预签名URL: bucket={}, object={}, expires={}秒", bucketName, objectName, expires);
String presignedUrl = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(objectName)
.expiry(expires, TimeUnit.SECONDS)
.build()
);
log.info("生成的预签名URL: {}", presignedUrl);
return presignedUrl;
} catch (Exception e) {
log.error("获取预签名URL失败: bucket={}, object={}, error={}", bucketName, objectName, e.getMessage());
return null;
}
}
/**
* 检查对象是否存在
*
* @param bucketName 存储桶名称
* @param objectName 对象名称
* @return 是否存在
*/
public boolean objectExists(String bucketName, String objectName) {
try {
minioClient.statObject(
StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build()
);
return true;
} catch (Exception e) {
log.debug("对象不存在: bucket={}, object={}, error={}", bucketName, objectName, e.getMessage());
return false;
}
}
/**
* 获取文件预签名URL默认7天过期
*
* @param bucketName 存储桶名称
* @param objectName 对象名称
* @return 预签名URL
*/
public String getPresignedObjectUrl(String bucketName, String objectName) {
return getPresignedObjectUrl(bucketName, objectName, 7 * 24 * 3600); // 7天
}
/**
* 复制文件
*
* @param sourceBucketName 源存储桶名称
* @param sourceObjectName 源对象名称
* @param targetBucketName 目标存储桶名称
* @param targetObjectName 目标对象名称
* @return 是否复制成功
*/
public boolean copyFile(String sourceBucketName, String sourceObjectName,
String targetBucketName, String targetObjectName) {
try {
createBucket(targetBucketName);
minioClient.copyObject(
CopyObjectArgs.builder()
.bucket(targetBucketName)
.object(targetObjectName)
.source(CopySource.builder()
.bucket(sourceBucketName)
.object(sourceObjectName)
.build())
.build()
);
log.info("文件复制成功: {}/{} -> {}/{}",
sourceBucketName, sourceObjectName, targetBucketName, targetObjectName);
return true;
} catch (Exception e) {
log.error("文件复制失败: {}", e.getMessage());
return false;
}
}
/**
* 生成唯一文件名
*
* @param originalFilename 原始文件名
* @return 唯一文件名
*/
private String generateFileName(String originalFilename) {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
String uuid = UUID.randomUUID().toString().replace("-", "");
String extension = "";
if (originalFilename != null && originalFilename.contains(".")) {
extension = originalFilename.substring(originalFilename.lastIndexOf("."));
}
return timestamp + "_" + uuid + extension;
}
/**
* 文件信息类
*/
public static class FileInfo {
private String objectName;
private Long size;
private java.time.ZonedDateTime lastModified;
private String etag;
// Getters and Setters
public String getObjectName() {
return objectName;
}
public void setObjectName(String objectName) {
this.objectName = objectName;
}
public Long getSize() {
return size;
}
public void setSize(Long size) {
this.size = size;
}
public java.time.ZonedDateTime getLastModified() {
return lastModified;
}
public void setLastModified(java.time.ZonedDateTime lastModified) {
this.lastModified = lastModified;
}
public String getEtag() {
return etag;
}
public void setEtag(String etag) {
this.etag = etag;
}
@Override
public String toString() {
return "FileInfo{" +
"objectName='" + objectName + '\'' +
", size=" + size +
", lastModified=" + lastModified +
", etag='" + etag + '\'' +
'}';
}
}
}