From ae584d54a63306840172add6da2643cf93d6a234 Mon Sep 17 00:00:00 2001 From: 疯狂的狮子Li <15040126243@163.com> Date: 星期一, 13 一月 2025 14:08:15 +0800 Subject: [PATCH] !635 合并 warmflow 功能分支 * update 优化 工作流设计器支持token传输 只需要放行token头获取即可 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 优化 无需多余set变量 * update 优化 避免重复处理 * update 优化 实体类隔离 * add 增加流程启动,办理接口 * update 调整流程驳回 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update warmflow 1.3.7 * update 增加 warmflow oracle pg sqlserver sql脚本文件 * update 增加 warmflow oracle pg sqlserver sql脚本文件 * add 新增workflow不同的sql语句 * add 新增添加租户同步默认流程定义 * update 优化 流程列表查询 删除无用mapper * update 导入流程 支持并发多文件上传 * update 调整流程定义查询 * update 优化 统一书写格式 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 优化发布事件增加租户ID * update 调整驳回记录 * Revert "update 获取用户简略信息,祖级部门列表,部门负责人等" * update 获取用户简略信息,祖级部门列表,部门负责人等 * update 更新warm-flow版本到v1.3.6-m1 * update 更新注释信息 * fix 临时修复 warm参数读取问题 * update warm-flow 1.3.4 => 1.3.5 优化流程图导入 * update 更新warm-flow版本到v1.3.5 2024-12-20 * update 增加抄送人名称 * update 我的抄送增加申请人以及更新时间 * update 优化监听事件注释 * update 优化流程分类名称翻译回显 * fix 修改根据流程分类id查询 * update 新增流程分类id查询 * fix 修复抄送错误 * fix 修复错误判空 * fix 修复错误判空 * update 新增删除流程事件 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 调整sql书写顺序 * fix 修复 抄送缺数据问题 与 已完成任务数据取错问题 * update 新增根据业务id查询流程实例详细信息 * update 调整变量参数 * update 调整分类接口 * update 统一业务id参数 * update 调整返回参数 * update 增加业务id通用查询条件 * update 优化代码 修复bug * update 优化新增流程分类判断 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 调整驳回 * update 优化错误注释 * update 优化流程分类sql语句 * update 调整驳回 * update 删除流程分类状态 * update 增加流程定义防重 * [fix] * update 优化代码 修复bug * update 优化流程定义增加类别树查询 * update 新增翻译根据流程分类ID查询流程分类名称 * update 根据分页对象构建表格分页数据对象 * update 优化流程定义列表名称 * update 优化流程分类校验 * update 导入流程 支持自定义类别 * update 流程案例 增加表单路径 * update 流程定义查询返回表单路径 * update 优化任务业务层 * fix 修复 请假天数不准确问题 * update 优化驳回 撤销 * update 删除类别查询权限 通用查询不需要加权限 * fix 修复 变量名修改错误 * Merge branch 'warm-flow-future' of https://gitee.com/dromara/RuoYi-Vue… * update 调整驳回 * update 表名类名统一命名 * Merge branch 'warm-flow-future' of https://gitee.com/dromara/RuoYi-Vue… * update 挑战者驳回 撤销 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * fix 修复会签 票签撤销问题 * update 调整并行环节撤销错误 增加办理校验 * Merge branch 'warm-flow-future' of https://gitee.com/dromara/RuoYi-Vue… * update 优化获取办理人 * update 优化 workflow模块增加doc依赖输出接口文档 * Revert "update 优化 删除无用方法" * Merge remote-tracking branch 'origin/dev' into warm-flow-future * Merge branch 'warm-flow-future' of https://gitee.com/dromara/RuoYi-Vue… * update 调整代办人查询错误 增加示例 * update 优化 重构代码 * update 优化 重构代码 * update 优化 将工作流消息推送改为sse * add 添加模型 * update 调整审批记录 * update 调整请假案例 * fix 修复流程定义查询错误 * update 调整流程实例查询错误 * update 调整获取当前登录任务实例 * add 添加消息发送 * update 调整办理监听 * [add] * [fix] * update 重构 将工作流查询逻辑封装为单独的service类 * [update] * [add] * 办理附件提交 * 申请人查询修改 * update 回退优化 删除无用方法 * update 优化任务办理人翻译实现 * update 优化任务分配人枚举 * update 优化 删除无用方法 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * Merge remote-tracking branch 'origin/dev' into warm-flow-future * [add] * update 优化全局任务办理监听 * update 删除无用引入 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 优化激活/挂起取反逻辑 * !614 style workflow xml 格式 * style workflow xml 格式 * !612 fix FlwInstanceMapper xml错误 * fix FlwInstanceMapper xml错误 * update 优化 后端代码 * update 优化接口请求路径 * update 调整已办排序 * add 添加任务作废 * update 调整任务办理操作 * update 调整加签,减签校验 * update 调整加签 * add 添加获取当前任务的办理人接口 * add 添加任务查询会签,票签比例 * update 调整任务,实例查询 * update 调整任务委托,转办,优化部分代码等 * update 调整流程定义视图 * add 添加任务,流程实例常用查询接口 * Merge branch 'dev' into warm-flow-future * update 优化工作流常量使用 * update 优化流程记录运行时长获取 * update 优化任务管理控制层 * update 回复业务状态枚举 * update 优化业务状态枚举 * update 调整昵称翻译 * update 调整修改办理人接口 * update 调整流程全局监听,调整任务办理人批量修改,优化代码 * update 优化查询可驳回节点 * update 优化添加抄送人 * update 优化增加人员类型枚举,删除无用常量 * update 优化请假天数工具类,删除缓存,加锁处理,可以采用外部传参的形式处理redis部分 * update 优化请假天数计算 * update 优化任务完成时间处理 * update 优化任务办理人获取 * update 优化任务操作,委派、转办、加签、减签、修改办理人等 * add 新增warm-flow-all.sql * del 删除多余SQL * del 删除无用vo * del 删除表单管理信息 * del 删除节点配置信息 * update 优化节点类型常量获取 * update 优化权限办理人获取 * update 优化权限办理人获取判断 * update 提交等待新版本待优化的开始监听信息 * update 新增获取部门负责人 * add 新增分派办理人监听器 * update 优化或者字符串用户ID * update 用户前缀去掉 * update 调整流程实例状态查询 * add 添加流程撤销 * add 添加流程抄送 * add 新增办理人权限处理器 * update 升级warm-flow1.3.4 * Merge branch 'dev' into warm-flow-future * update 调整流程定义复制 * update 调整流程启动设置启动人变量 * update 调整流程定义删除 * update 调整流程定义导入 * update 调整工作流任务,流程定义等查询 * update 调整工作流人员翻译查询 * update 调整转办,加签等参数 * update 调整流程办理设置办理人 * update 调整流程设计保存 * update 调整流程状态,移除过时方法,调整查询办理人 * update 补充工作流请求增加鉴权 * update 工作流请求增加鉴权 * update 新增流程完成监听器 * update 新增流程启动监听器 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 更新warm-flow版本 * update 优化workflow表sql格式 * update 优化,增加岗位权限判空处理 * update 当前用户所有权限岗位ID改为从token获取 * update 当前用户所有权限增加岗位ID权限 * update 更新通过岗位ID查询用户 * update 优化激活/挂起流程定义判断 * Revert "add 新增异常处理器和工作流封装包" * add 新增异常处理器和工作流封装包 * update 优化待办任务查询以及任务流转 * update 优化工作流工具,避免多次获取请求 * fix 修改无效标识 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 查询部门并返回任务指派的列表根据部门树搜索 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 优化任务办理人分组 * update 优化任务办理人查询 * update 优化任务办理人查询 * update 优化任务办理人 * update 完善任务办理人 * add 新增角色办理人 * !596 优化工作流代码逻辑 * update 优化工作流工具冗余代码 * update 优化工作流代码逻辑 * update 调整办理 驳回 终止等状态 * update 调整流程定义查询 * 解决冲突提交 warmflow代码 * update 升级warm-flow到1.3.0 调整流程办理 ,驳回,终止等 添加自定义监听 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 调整流程xml查询 * update 调整驳回 * update 升级warm-flow * update 调整任务办理设置办理人 * 调整转办,委托参数 * update warm-flow 1.2.4 => 1.2.7 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 调整抄送错误 * 添加已办,未办 * Merge branch 'warm-flow-future' of https://gitee.com/dromara/RuoYi-Vue… * add 添加我发起的单据接口 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 调整流程实例,待办查询 * remove 删除无用校验 * add 添加待办人查询 * Merge branch 'dev' into warm-flow-future * update 调整字段错误,流程导入 * add 添加流程激活/挂起接口 升级warm-flow到1.2.4 * add 添加历史流程定义查询 调整流程发布 * Merge remote-tracking branch 'origin/dev' into warm-flow-future * Merge remote-tracking branch 'origin/dev' into warm-flow-future * Merge remote-tracking branch 'origin/dev' into warm-flow-future * Merge remote-tracking branch 'origin/dev' into warm-flow-future * update 优化 TenantSpringCacheManager 处理逻辑 * fix 修复 一级缓存key未区分租户问题 * update 优化 角色权限判断 * update 更新 readme * update 优化 增加删除标志位常量优化查询代码 * fix 修复 登出无法正确删除对应的租户数据问题 * update 优化 sse 拦截网络中断io异常 * fix 修复 登录错误锁定不区分租户问题 * update 优化 sse 关闭连接各种异常问题 * update 优化 监控使用独立web依赖 * fix 修复 代码生成 错误匹配表名问题 * update 优化 适配 anyline 新改动 * update anyline 8.7.2-20240728 * update 脱敏策略优化增加密码 * add 新增 更多脱敏策略 * update 优化oss查询代码 * update 优化 sse发送消息 增加token有效期判断 * fix 修复 登出后重新登录 sse推送报错问题 * fix 修复 代码生成 数据源切换问题 * update anyline 8.7.2-20240726 * fix 修复 代码生成 表结构缓存问题 * update snailjob 1.1.0 => 1.1.1 * fix 修复 代码生成 表结构缓存问题 * update 优化 sse 自动装配 * 初始化添加warm-flow --- ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java | 566 +++++++++++++++++++++++++++++++++++++++---------------- 1 files changed, 397 insertions(+), 169 deletions(-) diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java index 53e05c9..1261b47 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/core/OssClient.java @@ -2,74 +2,116 @@ import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.IdUtil; -import com.amazonaws.ClientConfiguration; -import com.amazonaws.HttpMethod; -import com.amazonaws.Protocol; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import com.amazonaws.services.s3.model.*; +import org.dromara.common.core.constant.Constants; import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.FileUtils; import org.dromara.common.oss.constant.OssConstant; import org.dromara.common.oss.entity.UploadResult; import org.dromara.common.oss.enumd.AccessPolicyType; -import org.dromara.common.oss.enumd.PolicyType; import org.dromara.common.oss.exception.OssException; import org.dromara.common.oss.properties.OssProperties; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.ResponseInputStream; +import software.amazon.awssdk.core.async.AsyncResponseTransformer; +import software.amazon.awssdk.core.async.BlockingInputStreamAsyncRequestBody; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3Configuration; +import software.amazon.awssdk.services.s3.crt.S3CrtHttpConfiguration; +import software.amazon.awssdk.services.s3.model.GetObjectResponse; +import software.amazon.awssdk.services.s3.presigner.S3Presigner; +import software.amazon.awssdk.transfer.s3.S3TransferManager; +import software.amazon.awssdk.transfer.s3.model.*; +import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.InputStream; +import java.io.*; +import java.net.URI; import java.net.URL; -import java.util.Date; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; /** * S3 瀛樺偍鍗忚 鎵�鏈夊吋瀹筍3鍗忚鐨勪簯鍘傚晢鍧囨敮鎸� * 闃块噷浜� 鑵捐浜� 涓冪墰浜� minio * - * @author Lion Li + * @author AprilWind */ public class OssClient { + /** + * 鏈嶅姟鍟� + */ private final String configKey; + /** + * 閰嶇疆灞炴�� + */ private final OssProperties properties; - private final AmazonS3 client; + /** + * Amazon S3 寮傛瀹㈡埛绔� + */ + private final S3AsyncClient client; + /** + * 鐢ㄤ簬绠$悊 S3 鏁版嵁浼犺緭鐨勯珮绾у伐鍏� + */ + private final S3TransferManager transferManager; + + /** + * AWS S3 棰勭鍚� URL 鐨勭敓鎴愬櫒 + */ + private final S3Presigner presigner; + + /** + * 鏋勯�犳柟娉� + * + * @param configKey 閰嶇疆閿� + * @param ossProperties Oss閰嶇疆灞炴�� + */ public OssClient(String configKey, OssProperties ossProperties) { this.configKey = configKey; this.properties = ossProperties; try { - AwsClientBuilder.EndpointConfiguration endpointConfig = - new AwsClientBuilder.EndpointConfiguration(properties.getEndpoint(), properties.getRegion()); + // 鍒涘缓 AWS 璁よ瘉淇℃伅 + StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create( + AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey())); - AWSCredentials credentials = new BasicAWSCredentials(properties.getAccessKey(), properties.getSecretKey()); - AWSCredentialsProvider credentialsProvider = new AWSStaticCredentialsProvider(credentials); - ClientConfiguration clientConfig = new ClientConfiguration(); - if (OssConstant.IS_HTTPS.equals(properties.getIsHttps())) { - clientConfig.setProtocol(Protocol.HTTPS); - } else { - clientConfig.setProtocol(Protocol.HTTP); - } - AmazonS3ClientBuilder build = AmazonS3Client.builder() - .withEndpointConfiguration(endpointConfig) - .withClientConfiguration(clientConfig) - .withCredentials(credentialsProvider) - .disableChunkedEncoding(); - if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) { - // minio 浣跨敤https闄愬埗浣跨敤鍩熷悕璁块棶 闇�瑕佹閰嶇疆 绔欑偣濉煙鍚� - build.enablePathStyleAccess(); - } - this.client = build.build(); + // MinIO 浣跨敤 HTTPS 闄愬埗浣跨敤鍩熷悕璁块棶锛岀珯鐐瑰~鍩熷悕銆傞渶瑕佸惎鐢ㄨ矾寰勬牱寮忚闂� + boolean isStyle = !StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE); - createBucket(); + // 鍒涘缓AWS鍩轰簬 CRT 鐨� S3 瀹㈡埛绔� + this.client = S3AsyncClient.crtBuilder() + .credentialsProvider(credentialsProvider) + .endpointOverride(URI.create(getEndpoint())) + .region(of()) + .targetThroughputInGbps(20.0) + .minimumPartSizeInBytes(10 * 1025 * 1024L) + .checksumValidationEnabled(false) + .forcePathStyle(isStyle) + .httpConfiguration(S3CrtHttpConfiguration.builder() + .connectionTimeout(Duration.ofSeconds(60)) // 璁剧疆杩炴帴瓒呮椂 + .build()) + .build(); + + //AWS鍩轰簬 CRT 鐨� S3 AsyncClient 瀹炰緥鐢ㄤ綔 S3 浼犺緭绠$悊鍣ㄧ殑搴曞眰瀹㈡埛绔� + this.transferManager = S3TransferManager.builder().s3Client(this.client).build(); + + // 鍒涘缓 S3 閰嶇疆瀵硅薄 + S3Configuration config = S3Configuration.builder().chunkedEncodingEnabled(false) + .pathStyleAccessEnabled(isStyle).build(); + + // 鍒涘缓 棰勭鍚� URL 鐨勭敓鎴愬櫒 瀹炰緥锛岀敤浜庣敓鎴� S3 棰勭鍚� URL + this.presigner = S3Presigner.builder() + .region(of()) + .credentialsProvider(credentialsProvider) + .endpointOverride(URI.create(getDomain())) + .serviceConfiguration(config) + .build(); + } catch (Exception e) { if (e instanceof OssException) { throw e; @@ -78,126 +120,165 @@ } } - public void createBucket() { + /** + * 涓婁紶鏂囦欢鍒� Amazon S3锛屽苟杩斿洖涓婁紶缁撴灉 + * + * @param filePath 鏈湴鏂囦欢璺緞 + * @param key 鍦� Amazon S3 涓殑瀵硅薄閿� + * @param md5Digest 鏈湴鏂囦欢鐨� MD5 鍝堝笇鍊硷紙鍙�夛級 + * @param contentType 鏂囦欢鍐呭绫诲瀷 + * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅 + * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父 + */ + public UploadResult upload(Path filePath, String key, String md5Digest, String contentType) { try { - String bucketName = properties.getBucketName(); - if (client.doesBucketExistV2(bucketName)) { - return; - } - CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName); - AccessPolicyType accessPolicy = getAccessPolicy(); - createBucketRequest.setCannedAcl(accessPolicy.getAcl()); - client.createBucket(createBucketRequest); - client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType())); + // 鏋勫缓涓婁紶璇锋眰瀵硅薄 + FileUpload fileUpload = transferManager.uploadFile( + x -> x.putObjectRequest( + y -> y.bucket(properties.getBucketName()) + .key(key) + .contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null) + .contentType(contentType) + // 鐢ㄤ簬璁剧疆瀵硅薄鐨勮闂帶鍒跺垪琛紙ACL锛夈�備笉鍚屼簯鍘傚晢瀵笰CL鐨勬敮鎸佸拰瀹炵幇鏂瑰紡鏈夋墍涓嶅悓锛� + // 鍥犳鏍规嵁鍏蜂綋鐨勪簯鏈嶅姟鎻愪緵鍟嗭紝浣犲彲鑳介渶瑕佽繘琛屼笉鍚岀殑閰嶇疆锛堣嚜琛屽紑鍚紝闃块噷浜戞湁acl鏉冮檺閰嶇疆锛岃吘璁簯娌℃湁acl鏉冮檺閰嶇疆锛� + //.acl(getAccessPolicy().getObjectCannedACL()) + .build()) + .addTransferListener(LoggingTransferListener.create()) + .source(filePath).build()); + + // 绛夊緟涓婁紶瀹屾垚骞惰幏鍙栦笂浼犵粨鏋� + CompletedFileUpload uploadResult = fileUpload.completionFuture().join(); + String eTag = uploadResult.response().eTag(); + + // 鎻愬彇涓婁紶缁撴灉涓殑 ETag锛屽苟鏋勫缓涓�涓嚜瀹氫箟鐨� UploadResult 瀵硅薄 + return UploadResult.builder().url(getUrl() + StringUtils.SLASH + key).filename(key).eTag(eTag).build(); } catch (Exception e) { - throw new OssException("鍒涘缓Bucket澶辫触, 璇锋牳瀵归厤缃俊鎭�:[" + e.getMessage() + "]"); + // 鎹曡幏寮傚父骞舵姏鍑鸿嚜瀹氫箟寮傚父 + throw new OssException("涓婁紶鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]"); + } finally { + // 鏃犺涓婁紶鏄惁鎴愬姛锛屾渶缁堥兘浼氬垹闄や复鏃舵枃浠� + FileUtils.del(filePath); } } - public UploadResult upload(byte[] data, String path, String contentType) { - return upload(new ByteArrayInputStream(data), path, contentType); - } - - public UploadResult upload(InputStream inputStream, String path, String contentType) { + /** + * 涓婁紶 InputStream 鍒� Amazon S3 + * + * @param inputStream 瑕佷笂浼犵殑杈撳叆娴� + * @param key 鍦� Amazon S3 涓殑瀵硅薄閿� + * @param length 杈撳叆娴佺殑闀垮害 + * @param contentType 鏂囦欢鍐呭绫诲瀷 + * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅 + * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父 + */ + public UploadResult upload(InputStream inputStream, String key, Long length, String contentType) { + // 濡傛灉杈撳叆娴佷笉鏄� ByteArrayInputStream锛屽垯灏嗗叾璇诲彇涓哄瓧鑺傛暟缁勫啀鍒涘缓 ByteArrayInputStream if (!(inputStream instanceof ByteArrayInputStream)) { inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream)); } try { - ObjectMetadata metadata = new ObjectMetadata(); - metadata.setContentType(contentType); - metadata.setContentLength(inputStream.available()); - PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata); - // 璁剧疆涓婁紶瀵硅薄鐨� Acl 涓哄叕鍏辫 - putObjectRequest.setCannedAcl(getAccessPolicy().getAcl()); - client.putObject(putObjectRequest); + // 鍒涘缓寮傛璇锋眰浣擄紙length濡傛灉涓虹┖浼氭姤閿欙級 + BlockingInputStreamAsyncRequestBody body = BlockingInputStreamAsyncRequestBody.builder() + .contentLength(length) + .subscribeTimeout(Duration.ofSeconds(30)) + .build(); + + // 浣跨敤 transferManager 杩涜涓婁紶 + Upload upload = transferManager.upload( + x -> x.requestBody(body) + .putObjectRequest( + y -> y.bucket(properties.getBucketName()) + .key(key) + .contentType(contentType) + // 鐢ㄤ簬璁剧疆瀵硅薄鐨勮闂帶鍒跺垪琛紙ACL锛夈�備笉鍚屼簯鍘傚晢瀵笰CL鐨勬敮鎸佸拰瀹炵幇鏂瑰紡鏈夋墍涓嶅悓锛� + // 鍥犳鏍规嵁鍏蜂綋鐨勪簯鏈嶅姟鎻愪緵鍟嗭紝浣犲彲鑳介渶瑕佽繘琛屼笉鍚岀殑閰嶇疆锛堣嚜琛屽紑鍚紝闃块噷浜戞湁acl鏉冮檺閰嶇疆锛岃吘璁簯娌℃湁acl鏉冮檺閰嶇疆锛� + //.acl(getAccessPolicy().getObjectCannedACL()) + .build()) + .build()); + + // 灏嗚緭鍏ユ祦鍐欏叆璇锋眰浣� + body.writeInputStream(inputStream); + + // 绛夊緟鏂囦欢涓婁紶鎿嶄綔瀹屾垚 + CompletedUpload uploadResult = upload.completionFuture().join(); + String eTag = uploadResult.response().eTag(); + + // 鎻愬彇涓婁紶缁撴灉涓殑 ETag锛屽苟鏋勫缓涓�涓嚜瀹氫箟鐨� UploadResult 瀵硅薄 + return UploadResult.builder().url(getUrl() + StringUtils.SLASH + key).filename(key).eTag(eTag).build(); } catch (Exception e) { throw new OssException("涓婁紶鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]"); } - return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build(); - } - - public UploadResult upload(File file, String path) { - try { - PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, file); - // 璁剧疆涓婁紶瀵硅薄鐨� Acl 涓哄叕鍏辫 - putObjectRequest.setCannedAcl(getAccessPolicy().getAcl()); - client.putObject(putObjectRequest); - } catch (Exception e) { - throw new OssException("涓婁紶鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]"); - } - return UploadResult.builder().url(getUrl() + "/" + path).filename(path).build(); - } - - public void delete(String path) { - path = path.replace(getUrl() + "/", ""); - try { - client.deleteObject(properties.getBucketName(), path); - } catch (Exception e) { - throw new OssException("鍒犻櫎鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]"); - } - } - - public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) { - return upload(data, getPath(properties.getPrefix(), suffix), contentType); - } - - public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) { - return upload(inputStream, getPath(properties.getPrefix(), suffix), contentType); - } - - public UploadResult uploadSuffix(File file, String suffix) { - return upload(file, getPath(properties.getPrefix(), suffix)); } /** - * 鑾峰彇鏂囦欢鍏冩暟鎹� + * 涓嬭浇鏂囦欢浠� Amazon S3 鍒颁复鏃剁洰褰� * - * @param path 瀹屾暣鏂囦欢璺緞 + * @param path 鏂囦欢鍦� Amazon S3 涓殑瀵硅薄閿� + * @return 涓嬭浇鍚庣殑鏂囦欢鍦ㄦ湰鍦扮殑涓存椂璺緞 + * @throws OssException 濡傛灉涓嬭浇澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父 */ - public ObjectMetadata getObjectMetadata(String path) { - path = path.replace(getUrl() + "/", ""); - S3Object object = client.getObject(properties.getBucketName(), path); - return object.getObjectMetadata(); + public Path fileDownload(String path) { + // 鏋勫缓涓存椂鏂囦欢 + Path tempFilePath = FileUtils.createTempFile().toPath(); + // 浣跨敤 S3TransferManager 涓嬭浇鏂囦欢 + FileDownload downloadFile = transferManager.downloadFile( + x -> x.getObjectRequest( + y -> y.bucket(properties.getBucketName()) + .key(removeBaseUrl(path)) + .build()) + .addTransferListener(LoggingTransferListener.create()) + .destination(tempFilePath) + .build()); + // 绛夊緟鏂囦欢涓嬭浇鎿嶄綔瀹屾垚 + downloadFile.completionFuture().join(); + return tempFilePath; } - public InputStream getObjectContent(String path) { - path = path.replace(getUrl() + "/", ""); - S3Object object = client.getObject(properties.getBucketName(), path); - return object.getObjectContent(); - } - - public String getUrl() { - String domain = properties.getDomain(); - String endpoint = properties.getEndpoint(); - String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://"; - // 浜戞湇鍔″晢鐩存帴杩斿洖 - if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) { - if (StringUtils.isNotBlank(domain)) { - return header + domain; + /** + * 涓嬭浇鏂囦欢浠� Amazon S3 鍒� 杈撳嚭娴� + * + * @param key 鏂囦欢鍦� Amazon S3 涓殑瀵硅薄閿� + * @param out 杈撳嚭娴� + * @return 杈撳嚭娴佷腑鍐欏叆鐨勫瓧鑺傛暟锛堥暱搴︼級 + * @throws OssException 濡傛灉涓嬭浇澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父 + */ + public long download(String key, OutputStream out) { + try { + // 鏋勫缓涓嬭浇璇锋眰 + DownloadRequest<ResponseInputStream<GetObjectResponse>> downloadRequest = DownloadRequest.builder() + // 鏂囦欢瀵硅薄 + .getObjectRequest(y -> y.bucket(properties.getBucketName()) + .key(key) + .build()) + .addTransferListener(LoggingTransferListener.create()) + // 浣跨敤璁㈤槄杞崲鍣� + .responseTransformer(AsyncResponseTransformer.toBlockingInputStream()) + .build(); + // 浣跨敤 S3TransferManager 涓嬭浇鏂囦欢 + Download<ResponseInputStream<GetObjectResponse>> responseFuture = transferManager.download(downloadRequest); + // 杈撳嚭鍒版祦涓� + try (ResponseInputStream<GetObjectResponse> responseStream = responseFuture.completionFuture().join().result()) { // auto-closeable stream + return responseStream.transferTo(out); // 闃诲璋冪敤绾跨▼ blocks the calling thread } - return header + properties.getBucketName() + "." + endpoint; + } catch (Exception e) { + throw new OssException("鏂囦欢涓嬭浇澶辫触锛岄敊璇俊鎭�:[" + e.getMessage() + "]"); } - // minio 鍗曠嫭澶勭悊 - if (StringUtils.isNotBlank(domain)) { - return header + domain + "/" + properties.getBucketName(); - } - return header + endpoint + "/" + properties.getBucketName(); } - public String getPath(String prefix, String suffix) { - // 鐢熸垚uuid - String uuid = IdUtil.fastSimpleUUID(); - // 鏂囦欢璺緞 - String path = DateUtils.datePath() + "/" + uuid; - if (StringUtils.isNotBlank(prefix)) { - path = prefix + "/" + path; + /** + * 鍒犻櫎浜戝瓨鍌ㄦ湇鍔′腑鎸囧畾璺緞涓嬫枃浠� + * + * @param path 鎸囧畾璺緞 + */ + public void delete(String path) { + try { + client.deleteObject( + x -> x.bucket(properties.getBucketName()) + .key(removeBaseUrl(path)) + .build()); + } catch (Exception e) { + throw new OssException("鍒犻櫎鏂囦欢澶辫触锛岃妫�鏌ラ厤缃俊鎭�:[" + e.getMessage() + "]"); } - return path + suffix; - } - - - public String getConfigKey() { - return configKey; } /** @@ -207,12 +288,187 @@ * @param second 鎺堟潈鏃堕棿 */ public String getPrivateUrl(String objectKey, Integer second) { - GeneratePresignedUrlRequest generatePresignedUrlRequest = - new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey) - .withMethod(HttpMethod.GET) - .withExpiration(new Date(System.currentTimeMillis() + 1000L * second)); - URL url = client.generatePresignedUrl(generatePresignedUrlRequest); + // 浣跨敤 AWS S3 棰勭鍚� URL 鐨勭敓鎴愬櫒 鑾峰彇瀵硅薄鐨勯绛惧悕 URL + URL url = presigner.presignGetObject( + x -> x.signatureDuration(Duration.ofSeconds(second)) + .getObjectRequest( + y -> y.bucket(properties.getBucketName()) + .key(objectKey) + .build()) + .build()) + .url(); return url.toString(); + } + + /** + * 涓婁紶 byte[] 鏁版嵁鍒� Amazon S3锛屼娇鐢ㄦ寚瀹氱殑鍚庣紑鏋勯�犲璞¢敭銆� + * + * @param data 瑕佷笂浼犵殑 byte[] 鏁版嵁 + * @param suffix 瀵硅薄閿殑鍚庣紑 + * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅 + * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父 + */ + public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) { + return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Long.valueOf(data.length), contentType); + } + + /** + * 涓婁紶 InputStream 鍒� Amazon S3锛屼娇鐢ㄦ寚瀹氱殑鍚庣紑鏋勯�犲璞¢敭銆� + * + * @param inputStream 瑕佷笂浼犵殑杈撳叆娴� + * @param suffix 瀵硅薄閿殑鍚庣紑 + * @param length 杈撳叆娴佺殑闀垮害 + * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅 + * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父 + */ + public UploadResult uploadSuffix(InputStream inputStream, String suffix, Long length, String contentType) { + return upload(inputStream, getPath(properties.getPrefix(), suffix), length, contentType); + } + + /** + * 涓婁紶鏂囦欢鍒� Amazon S3锛屼娇鐢ㄦ寚瀹氱殑鍚庣紑鏋勯�犲璞¢敭 + * + * @param file 瑕佷笂浼犵殑鏂囦欢 + * @param suffix 瀵硅薄閿殑鍚庣紑 + * @return UploadResult 鍖呭惈涓婁紶鍚庣殑鏂囦欢淇℃伅 + * @throws OssException 濡傛灉涓婁紶澶辫触锛屾姏鍑鸿嚜瀹氫箟寮傚父 + */ + public UploadResult uploadSuffix(File file, String suffix) { + return upload(file.toPath(), getPath(properties.getPrefix(), suffix), null, FileUtils.getMimeType(suffix)); + } + + /** + * 鑾峰彇鏂囦欢杈撳叆娴� + * + * @param path 瀹屾暣鏂囦欢璺緞 + * @return 杈撳叆娴� + */ + public InputStream getObjectContent(String path) throws IOException { + // 涓嬭浇鏂囦欢鍒颁复鏃剁洰褰� + Path tempFilePath = fileDownload(path); + // 鍒涘缓杈撳叆娴� + InputStream inputStream = Files.newInputStream(tempFilePath); + // 鍒犻櫎涓存椂鏂囦欢 + FileUtils.del(tempFilePath); + // 杩斿洖瀵硅薄鍐呭鐨勮緭鍏ユ祦 + return inputStream; + } + + /** + * 鑾峰彇 S3 瀹㈡埛绔殑缁堢鐐� URL + * + * @return 缁堢鐐� URL + */ + public String getEndpoint() { + // 鏍规嵁閰嶇疆鏂囦欢涓殑鏄惁浣跨敤 HTTPS锛岃缃崗璁ご閮� + String header = getIsHttps(); + // 鎷兼帴鍗忚澶撮儴鍜岀粓绔偣锛屽緱鍒板畬鏁寸殑缁堢鐐� URL + return header + properties.getEndpoint(); + } + + /** + * 鑾峰彇 S3 瀹㈡埛绔殑缁堢鐐� URL锛堣嚜瀹氫箟鍩熷悕锛� + * + * @return 缁堢鐐� URL + */ + public String getDomain() { + // 浠庨厤缃腑鑾峰彇鍩熷悕銆佺粓绔偣銆佹槸鍚︿娇鐢� HTTPS 绛変俊鎭� + String domain = properties.getDomain(); + String endpoint = properties.getEndpoint(); + String header = getIsHttps(); + + // 濡傛灉鏄簯鏈嶅姟鍟嗭紝鐩存帴杩斿洖鍩熷悕鎴栫粓绔偣 + if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) { + return StringUtils.isNotEmpty(domain) ? header + domain : header + endpoint; + } + + // 濡傛灉鏄� MinIO锛屽鐞嗗煙鍚嶅苟杩斿洖 + if (StringUtils.isNotEmpty(domain)) { + return domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP) ? domain : header + domain; + } + + // 杩斿洖缁堢鐐� + return header + endpoint; + } + + /** + * 鏍规嵁浼犲叆鐨� region 鍙傛暟杩斿洖鐩稿簲鐨� AWS 鍖哄煙 + * 濡傛灉 region 鍙傛暟闈炵┖锛屼娇鐢� Region.of 鏂规硶鍒涘缓骞惰繑鍥炲搴旂殑 AWS 鍖哄煙瀵硅薄 + * 濡傛灉 region 鍙傛暟涓虹┖锛岃繑鍥炰竴涓粯璁ょ殑 AWS 鍖哄煙锛堜緥濡傦紝us-east-1锛夛紝浣滀负骞挎硾鏀寔鐨勫尯鍩� + * + * @return 瀵瑰簲鐨� AWS 鍖哄煙瀵硅薄锛屾垨鑰呴粯璁ょ殑骞挎硾鏀寔鐨勫尯鍩燂紙us-east-1锛� + */ + public Region of() { + //AWS 鍖哄煙瀛楃涓� + String region = properties.getRegion(); + // 濡傛灉 region 鍙傛暟闈炵┖锛屼娇鐢� Region.of 鏂规硶鍒涘缓瀵瑰簲鐨� AWS 鍖哄煙瀵硅薄锛屽惁鍒欒繑鍥為粯璁ゅ尯鍩� + return StringUtils.isNotEmpty(region) ? Region.of(region) : Region.US_EAST_1; + } + + /** + * 鑾峰彇浜戝瓨鍌ㄦ湇鍔$殑URL + * + * @return 鏂囦欢璺緞 + */ + public String getUrl() { + String domain = properties.getDomain(); + String endpoint = properties.getEndpoint(); + String header = getIsHttps(); + // 浜戞湇鍔″晢鐩存帴杩斿洖 + if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) { + return header + (StringUtils.isNotEmpty(domain) ? domain : properties.getBucketName() + "." + endpoint); + } + // MinIO 鍗曠嫭澶勭悊 + if (StringUtils.isNotEmpty(domain)) { + // 濡傛灉 domain 浠� "https://" 鎴� "http://" 寮�澶� + return (domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP)) ? + domain + StringUtils.SLASH + properties.getBucketName() : header + domain + StringUtils.SLASH + properties.getBucketName(); + } + return header + endpoint + StringUtils.SLASH + properties.getBucketName(); + } + + /** + * 鐢熸垚涓�涓鍚堢壒瀹氳鍒欑殑銆佸敮涓�鐨勬枃浠惰矾寰勩�傞�氳繃浣跨敤鏃ユ湡銆乁UID銆佸墠缂�鍜屽悗缂�绛夊厓绱犵殑缁勫悎锛岀‘淇濅簡鏂囦欢璺緞鐨勭嫭涓�鏃犱簩鎬� + * + * @param prefix 鍓嶇紑 + * @param suffix 鍚庣紑 + * @return 鏂囦欢璺緞 + */ + public String getPath(String prefix, String suffix) { + // 鐢熸垚uuid + String uuid = IdUtil.fastSimpleUUID(); + // 鐢熸垚鏃ユ湡璺緞 + String datePath = DateUtils.datePath(); + // 鎷兼帴璺緞 + String path = StringUtils.isNotEmpty(prefix) ? + prefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid : datePath + StringUtils.SLASH + uuid; + return path + suffix; + } + + /** + * 绉婚櫎璺緞涓殑鍩虹URL閮ㄥ垎锛屽緱鍒扮浉瀵硅矾寰� + * + * @param path 瀹屾暣鐨勮矾寰勶紝鍖呮嫭鍩虹URL鍜岀浉瀵硅矾寰� + * @return 鍘婚櫎鍩虹URL鍚庣殑鐩稿璺緞 + */ + public String removeBaseUrl(String path) { + return path.replace(getUrl() + StringUtils.SLASH, ""); + } + + /** + * 鏈嶅姟鍟� + */ + public String getConfigKey() { + return configKey; + } + + /** + * 鑾峰彇鏄惁浣跨敤 HTTPS 鐨勯厤缃紝骞惰繑鍥炵浉搴旂殑鍗忚澶撮儴銆� + * + * @return 鍗忚澶撮儴锛屾牴鎹槸鍚︿娇鐢� HTTPS 杩斿洖 "https://" 鎴� "http://" + */ + public String getIsHttps() { + return OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? Constants.HTTPS : Constants.HTTP; } /** @@ -229,34 +485,6 @@ */ public AccessPolicyType getAccessPolicy() { return AccessPolicyType.getByType(properties.getAccessPolicy()); - } - - private static String getPolicy(String bucketName, PolicyType policyType) { - StringBuilder builder = new StringBuilder(); - builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n"); - builder.append(switch (policyType) { - case WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucketMultipartUploads\"\n"; - case READ_WRITE -> "\"s3:GetBucketLocation\",\n\"s3:ListBucket\",\n\"s3:ListBucketMultipartUploads\"\n"; - default -> "\"s3:GetBucketLocation\"\n"; - }); - builder.append("],\n\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); - builder.append(bucketName); - builder.append("\"\n},\n"); - if (policyType == PolicyType.READ) { - builder.append("{\n\"Action\": [\n\"s3:ListBucket\"\n],\n\"Effect\": \"Deny\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); - builder.append(bucketName); - builder.append("\"\n},\n"); - } - builder.append("{\n\"Action\": "); - builder.append(switch (policyType) { - case WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"; - case READ_WRITE -> "[\n\"s3:AbortMultipartUpload\",\n\"s3:DeleteObject\",\n\"s3:GetObject\",\n\"s3:ListMultipartUploadParts\",\n\"s3:PutObject\"\n],\n"; - default -> "\"s3:GetObject\",\n"; - }); - builder.append("\"Effect\": \"Allow\",\n\"Principal\": \"*\",\n\"Resource\": \"arn:aws:s3:::"); - builder.append(bucketName); - builder.append("/*\"\n}\n],\n\"Version\": \"2012-10-17\"\n}\n"); - return builder.toString(); } } -- Gitblit v1.9.3