diff --git a/kernel-d-file/file-sdk-minio/pom.xml b/kernel-d-file/file-sdk-minio/pom.xml index 0538ec58f..84770658e 100644 --- a/kernel-d-file/file-sdk-minio/pom.xml +++ b/kernel-d-file/file-sdk-minio/pom.xml @@ -33,10 +33,6 @@ - - com.amazonaws - aws-java-sdk-s3 - io.minio minio diff --git a/kernel-d-file/file-sdk-minio/src/main/java/cn/stylefeng/roses/kernel/file/minio/MinIoFileOperator.java b/kernel-d-file/file-sdk-minio/src/main/java/cn/stylefeng/roses/kernel/file/minio/MinIoFileOperator.java index 762dcdd77..931d2ee91 100644 --- a/kernel-d-file/file-sdk-minio/src/main/java/cn/stylefeng/roses/kernel/file/minio/MinIoFileOperator.java +++ b/kernel-d-file/file-sdk-minio/src/main/java/cn/stylefeng/roses/kernel/file/minio/MinIoFileOperator.java @@ -36,14 +36,14 @@ import cn.stylefeng.roses.kernel.file.api.exception.FileException; import cn.stylefeng.roses.kernel.file.api.exception.enums.FileExceptionEnum; import cn.stylefeng.roses.kernel.file.api.expander.FileConfigExpander; import cn.stylefeng.roses.kernel.file.api.pojo.props.MinIoProperties; +import cn.stylefeng.roses.kernel.file.minio.factory.MinIoConfigFactory; import cn.stylefeng.roses.kernel.rule.util.HttpServletUtil; -import io.minio.MinioClient; -import io.minio.errors.InvalidEndpointException; -import io.minio.errors.InvalidPortException; -import io.minio.policy.PolicyType; +import io.minio.*; +import lombok.extern.slf4j.Slf4j; import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.rmi.NoSuchObjectException; import java.util.HashMap; import java.util.Map; @@ -53,6 +53,7 @@ import java.util.Map; * @author fengshuonan * @since 2020/10/31 10:35 */ +@Slf4j public class MinIoFileOperator implements FileOperatorApi { private final Object LOCK = new Object(); @@ -84,12 +85,7 @@ public class MinIoFileOperator implements FileOperatorApi { String secretKey = minIoProperties.getSecretKey(); // 创建minioClient实例 - try { - minioClient = new MinioClient(endpoint, accessKey, secretKey); - } catch (InvalidEndpointException | InvalidPortException e) { - // 组装提示信息 - throw new FileException(FileExceptionEnum.MINIO_FILE_ERROR, e.getMessage()); - } + minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build(); } @Override @@ -105,52 +101,85 @@ public class MinIoFileOperator implements FileOperatorApi { @Override public boolean doesBucketExist(String bucketName) { try { - return minioClient.bucketExists(bucketName); + return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); } catch (Exception e) { - // 组装提示信息 - throw new FileException(FileExceptionEnum.MINIO_FILE_ERROR, e.getMessage()); + log.error("MinIo校验桶是否存在时异常:", e); + return false; } } @Override public void setBucketAcl(String bucketName, BucketAuthEnum bucketAuthEnum) { - setFileAcl(bucketName, "*", bucketAuthEnum); + try { + // 创建规则的json + String policyJson = MinIoConfigFactory.createPolicyJson(bucketAuthEnum, bucketName); + + // 使用 SetBucketPolicyArgs 设置策略 + SetBucketPolicyArgs args = SetBucketPolicyArgs.builder() + .bucket(bucketName) + .config(policyJson) + .build(); + minioClient.setBucketPolicy(args); + } catch (Exception e) { + log.error("Error occurred while setting bucket ACL: ", e); + throw new FileException(FileExceptionEnum.MINIO_FILE_ERROR, e.getMessage()); + } } @Override public boolean isExistingFile(String bucketName, String key) { - InputStream inputStream = null; try { - inputStream = minioClient.getObject(bucketName, key); - if (inputStream != null) { - return true; - } - } catch (Exception e) { + // 使用 StatObjectArgs 构建查询参数 + StatObjectResponse response = minioClient.statObject( + StatObjectArgs.builder() + .bucket(bucketName) + .object(key) + .build() + ); + // 如果文件存在,statObject 不会抛出异常,返回 true + return true; + } catch (NoSuchObjectException e) { + // 如果文件不存在,会抛出 NoSuchObjectException,返回 false + return false; + } catch (Exception e) { + log.error("MINIO无法找到文件! ", e); return false; - } finally { - IoUtil.close(inputStream); } - return false; } @Override public void storageFile(String bucketName, String key, byte[] bytes) { - if (bytes != null && bytes.length > 0) { - // 字节数组转字节数组输入流 + if (bytes == null || bytes.length == 0) { + return; + } + + try { + // 检查存储桶是否存在,不存在直接返回 + boolean isBucketExist = this.doesBucketExist(bucketName); + if (!isBucketExist) { + log.error("无法存储文件到桶,桶不存在!桶名称:{}", bucketName); + return; + } + + // 将字节数组转换为输入流 ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(bytes); - // 获取文件类型 - ByteArrayInputStream tmp = IoUtil.toStream(bytes); - String type = FileTypeUtil.getType(tmp); + String type = FileTypeUtil.getType(byteArrayInputStream); String fileContentType = getFileContentType(String.format("%s%s", ".", type)); - try { - minioClient.putObject(bucketName, key, byteArrayInputStream, bytes.length, fileContentType); - } catch (Exception e) { - - // 组装提示信息 - throw new FileException(FileExceptionEnum.MINIO_FILE_ERROR, e.getMessage()); - } + // 上传文件 + minioClient.putObject( + PutObjectArgs.builder() + .bucket(bucketName) + .object(key) + // -1 表示不限制流的大小 + .stream(byteArrayInputStream, bytes.length, -1) + .contentType(fileContentType) + .build() + ); + } catch (Exception e) { + log.error("MINIO文件操作异常:", e); + throw new FileException(FileExceptionEnum.MINIO_FILE_ERROR, e.getMessage()); } } @@ -165,10 +194,10 @@ public class MinIoFileOperator implements FileOperatorApi { @Override public byte[] getFileBytes(String bucketName, String key) { try { - InputStream inputStream = minioClient.getObject(bucketName, key); + InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(key).build()); return IoUtil.readBytes(inputStream); } catch (Exception e) { - // 组装提示信息 + log.error("MINIO文件操作异常:", e); throw new FileException(FileExceptionEnum.MINIO_FILE_ERROR, e.getMessage()); } } @@ -176,18 +205,15 @@ public class MinIoFileOperator implements FileOperatorApi { @Override public void setFileAcl(String bucketName, String key, BucketAuthEnum bucketAuthEnum) { try { - if (bucketAuthEnum.equals(BucketAuthEnum.PRIVATE)) { - minioClient.setBucketPolicy(bucketName, key, PolicyType.NONE); - } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ)) { - minioClient.setBucketPolicy(bucketName, key, PolicyType.READ_ONLY); - } else if (bucketAuthEnum.equals(BucketAuthEnum.PUBLIC_READ_WRITE)) { - minioClient.setBucketPolicy(bucketName, key, PolicyType.READ_WRITE); - } else if (bucketAuthEnum.equals(BucketAuthEnum.MINIO_WRITE_ONLY)) { - minioClient.setBucketPolicy(bucketName, key, PolicyType.WRITE_ONLY); - } + // 根据枚举值设置对应的策略 + String policyJson = MinIoConfigFactory.createKeyPolicyJson(bucketName, key, bucketAuthEnum); + SetBucketPolicyArgs args = SetBucketPolicyArgs.builder() + .bucket(bucketName) + .config(policyJson) + .build(); + minioClient.setBucketPolicy(args); } catch (Exception e) { - - // 组装提示信息 + log.error("MINIO文件操作异常:", e); throw new FileException(FileExceptionEnum.MINIO_FILE_ERROR, e.getMessage()); } } @@ -195,9 +221,23 @@ public class MinIoFileOperator implements FileOperatorApi { @Override public void copyFile(String originBucketName, String originFileKey, String newBucketName, String newFileKey) { try { - minioClient.copyObject(originBucketName, originFileKey, newBucketName, newFileKey); + // 构建源文件的 CopySource 对象 + CopySource copySource = CopySource.builder() + .bucket(originBucketName) + .object(originFileKey) + .build(); + + // 构建拷贝文件的目标参数 + CopyObjectArgs copyObjectArgs = CopyObjectArgs.builder() + .source(copySource) + .bucket(newBucketName) + .object(newFileKey) + .build(); + + // 执行拷贝操作 + minioClient.copyObject(copyObjectArgs); } catch (Exception e) { - // 组装提示信息 + log.error("MINIO文件操作异常:", e); throw new FileException(FileExceptionEnum.MINIO_FILE_ERROR, e.getMessage()); } } @@ -211,8 +251,12 @@ public class MinIoFileOperator implements FileOperatorApi { // 获取context-path String contextPath = HttpServletUtil.getRequest().getContextPath(); - return FileConfigExpander.getServerDeployHost() + contextPath + FileConstants.FILE_PREVIEW_BY_OBJECT_NAME + "?fileBucket=" + bucketName + "&fileObjectName=" + key + "&token=" + token; - + return FileConfigExpander.getServerDeployHost() + + contextPath + + FileConstants.FILE_PREVIEW_BY_OBJECT_NAME + + "?fileBucket=" + bucketName + + "&fileObjectName=" + key + + "&token=" + token; } @Override @@ -220,18 +264,21 @@ public class MinIoFileOperator implements FileOperatorApi { // 获取context-path String contextPath = HttpServletUtil.getRequest().getContextPath(); - return FileConfigExpander.getServerDeployHost() + contextPath + FileConstants.FILE_PREVIEW_BY_OBJECT_NAME + "?fileBucket=" + bucketName + "&fileObjectName=" + key; + return FileConfigExpander.getServerDeployHost() + + contextPath + + FileConstants.FILE_PREVIEW_BY_OBJECT_NAME + + "?fileBucket=" + bucketName + + "&fileObjectName=" + key; } @Override public void deleteFile(String bucketName, String key) { try { - minioClient.removeObject(bucketName, key); + minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(key).build()); } catch (Exception e) { - // 组装提示信息 + log.error("MINIO文件操作异常:", e); throw new FileException(FileExceptionEnum.MINIO_FILE_ERROR, e.getMessage()); } - } @Override @@ -247,7 +294,7 @@ public class MinIoFileOperator implements FileOperatorApi { */ private Map getFileContentType() { synchronized (LOCK) { - if (contentType.size() == 0) { + if (contentType.isEmpty()) { contentType.put(".bmp", "application/x-bmp"); contentType.put(".gif", "image/gif"); contentType.put(".fax", "image/fax"); diff --git a/kernel-d-file/file-sdk-minio/src/main/java/cn/stylefeng/roses/kernel/file/minio/factory/MinIoConfigFactory.java b/kernel-d-file/file-sdk-minio/src/main/java/cn/stylefeng/roses/kernel/file/minio/factory/MinIoConfigFactory.java new file mode 100644 index 000000000..7caeb8b4e --- /dev/null +++ b/kernel-d-file/file-sdk-minio/src/main/java/cn/stylefeng/roses/kernel/file/minio/factory/MinIoConfigFactory.java @@ -0,0 +1,157 @@ +package cn.stylefeng.roses.kernel.file.minio.factory; + +import cn.stylefeng.roses.kernel.file.api.enums.BucketAuthEnum; + +/** + * MinIO配置工厂 + * + * @author fengshuonan + * @since 2025/4/30 21:56 + */ +public class MinIoConfigFactory { + + /** + * 生成桶的策略JSON + * + * @author fengshuonan + * @since 2025/4/30 21:53 + */ + public static String createPolicyJson(BucketAuthEnum bucketAuthEnum, String bucketName) { + switch (bucketAuthEnum) { + case PRIVATE: + return "{\n" + + " \"Version\": \"2012-10-17\",\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Effect\": \"Deny\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": \"s3:*\",\n" + + " \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" + + " }\n" + + " ]\n" + + "}"; + case PUBLIC_READ: + return "{\n" + + " \"Version\": \"2012-10-17\",\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Effect\": \"Allow\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": \"s3:GetObject\",\n" + + " \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" + + " }\n" + + " ]\n" + + "}"; + case PUBLIC_READ_WRITE: + return "{\n" + + " \"Version\": \"2012-10-17\",\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Effect\": \"Allow\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": [\n" + + " \"s3:GetObject\",\n" + + " \"s3:PutObject\"\n" + + " ],\n" + + " \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" + + " }\n" + + " ]\n" + + "}"; + case MINIO_WRITE_ONLY: + return "{\n" + + " \"Version\": \"2012-10-17\",\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Effect\": \"Allow\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": \"s3:PutObject\",\n" + + " \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" + + " },\n" + + " {\n" + + " \"Effect\": \"Deny\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": \"s3:GetObject\",\n" + + " \"Resource\": \"arn:aws:s3:::" + bucketName + "/*\"\n" + + " }\n" + + " ]\n" + + "}"; + default: + throw new IllegalArgumentException("Unsupported bucket auth enum: " + bucketAuthEnum); + } + } + + /** + * 生成文件的策略JSON + * + * @author fengshuonan + * @since 2025/4/30 22:15 + */ + public static String createKeyPolicyJson(String bucketName, String key, BucketAuthEnum bucketAuthEnum) { + switch (bucketAuthEnum) { + case PRIVATE: + return String.format( + "{\n" + + " \"Version\": \"2012-10-17\",\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Effect\": \"Deny\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": \"s3:*\",\n" + + " \"Resource\": \"arn:aws:s3:::%s/%s\"\n" + + " }\n" + + " ]\n" + + "}", bucketName, key); + case PUBLIC_READ: + return String.format( + "{\n" + + " \"Version\": \"2012-10-17\",\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Effect\": \"Allow\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": \"s3:GetObject\",\n" + + " \"Resource\": \"arn:aws:s3:::%s/%s\"\n" + + " }\n" + + " ]\n" + + "}", bucketName, key); + case PUBLIC_READ_WRITE: + return String.format( + "{\n" + + " \"Version\": \"2012-10-17\",\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Effect\": \"Allow\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": [\n" + + " \"s3:GetObject\",\n" + + " \"s3:PutObject\"\n" + + " ],\n" + + " \"Resource\": \"arn:aws:s3:::%s/%s\"\n" + + " }\n" + + " ]\n" + + "}", bucketName, key); + case MINIO_WRITE_ONLY: + return String.format( + "{\n" + + " \"Version\": \"2012-10-17\",\n" + + " \"Statement\": [\n" + + " {\n" + + " \"Effect\": \"Allow\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": \"s3:PutObject\",\n" + + " \"Resource\": \"arn:aws:s3:::%s/%s\"\n" + + " },\n" + + " {\n" + + " \"Effect\": \"Deny\",\n" + + " \"Principal\": \"*\",\n" + + " \"Action\": \"s3:GetObject\",\n" + + " \"Resource\": \"arn:aws:s3:::%s/%s\"\n" + + " }\n" + + " ]\n" + + "}", bucketName, key); + default: + throw new IllegalArgumentException("Unsupported bucket auth enum: " + bucketAuthEnum); + } + } + +} diff --git a/pom.xml b/pom.xml index f5cc62f54..681e06763 100644 --- a/pom.xml +++ b/pom.xml @@ -129,8 +129,7 @@ 2.5.1 3.1.57 7.9.2 - 1.11.106 - 3.0.10 + 8.5.17 4.5.2 1.6.2 3.0.22 @@ -253,13 +252,6 @@ - - com.amazonaws - aws-java-sdk-bom - ${aws.sdk.version} - pom - import - io.minio minio