Commit f141d64eb2bd755a44564157d3c5254506230d11

Authored by puhui999
1 parent d83b7cd5

【依赖升级】AWS SDK for Java 1.x to 2.x

yudao-dependencies/pom.xml
@@ -68,7 +68,7 @@ @@ -68,7 +68,7 @@
68 <!-- 三方云服务相关 --> 68 <!-- 三方云服务相关 -->
69 <commons-io.version>2.17.0</commons-io.version> 69 <commons-io.version>2.17.0</commons-io.version>
70 <commons-compress.version>1.27.1</commons-compress.version> 70 <commons-compress.version>1.27.1</commons-compress.version>
71 - <aws-java-sdk-s3.version>1.12.777</aws-java-sdk-s3.version> 71 + <awssdk.version>2.30.14</awssdk.version>
72 <justauth.version>2.0.5</justauth.version> 72 <justauth.version>2.0.5</justauth.version>
73 <jimureport.version>1.8.1</jimureport.version> 73 <jimureport.version>1.8.1</jimureport.version>
74 <weixin-java.version>4.6.0</weixin-java.version> 74 <weixin-java.version>4.6.0</weixin-java.version>
@@ -91,6 +91,13 @@ @@ -91,6 +91,13 @@
91 <type>pom</type> 91 <type>pom</type>
92 <scope>import</scope> 92 <scope>import</scope>
93 </dependency> 93 </dependency>
  94 + <dependency>
  95 + <groupId>software.amazon.awssdk</groupId>
  96 + <artifactId>bom</artifactId>
  97 + <version>${awssdk.version}</version>
  98 + <type>pom</type>
  99 + <scope>import</scope>
  100 + </dependency>
94 101
95 <!-- 业务组件 --> 102 <!-- 业务组件 -->
96 <dependency> 103 <dependency>
@@ -538,13 +545,6 @@ @@ -538,13 +545,6 @@
538 <version>${jsoup.version}</version> 545 <version>${jsoup.version}</version>
539 </dependency> 546 </dependency>
540 547
541 - <!-- 三方云服务相关 -->  
542 - <dependency>  
543 - <groupId>com.amazonaws</groupId>  
544 - <artifactId>aws-java-sdk-s3</artifactId>  
545 - <version>${aws-java-sdk-s3.version}</version>  
546 - </dependency>  
547 -  
548 <dependency> 548 <dependency>
549 <groupId>com.xingyuv</groupId> 549 <groupId>com.xingyuv</groupId>
550 <artifactId>spring-boot-starter-justauth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) --> 550 <artifactId>spring-boot-starter-justauth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) -->
yudao-module-infra/yudao-module-infra-biz/pom.xml
@@ -115,9 +115,18 @@ @@ -115,9 +115,18 @@
115 <groupId>com.jcraft</groupId> 115 <groupId>com.jcraft</groupId>
116 <artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 --> 116 <artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 -->
117 </dependency> 117 </dependency>
  118 + <!-- 文件客户端:解决阿里云、腾讯云、minio 等 S3 连接 -->
118 <dependency> 119 <dependency>
119 - <groupId>com.amazonaws</groupId>  
120 - <artifactId>aws-java-sdk-s3</artifactId><!-- 文件客户端:解决阿里云、腾讯云、minio 等 S3 连接 --> 120 + <groupId>software.amazon.awssdk</groupId>
  121 + <artifactId>s3</artifactId>
  122 + </dependency>
  123 + <dependency>
  124 + <groupId>software.amazon.awssdk</groupId>
  125 + <artifactId>auth</artifactId>
  126 + </dependency>
  127 + <dependency>
  128 + <groupId>software.amazon.awssdk</groupId>
  129 + <artifactId>regions</artifactId>
121 </dependency> 130 </dependency>
122 131
123 <dependency> 132 <dependency>
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java
@@ -4,65 +4,68 @@ import cn.hutool.core.io.IoUtil; @@ -4,65 +4,68 @@ import cn.hutool.core.io.IoUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.hutool.http.HttpUtil; 5 import cn.hutool.http.HttpUtil;
6 import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; 6 import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
7 -import com.amazonaws.HttpMethod;  
8 -import com.amazonaws.auth.AWSStaticCredentialsProvider;  
9 -import com.amazonaws.auth.BasicAWSCredentials;  
10 -import com.amazonaws.client.builder.AwsClientBuilder;  
11 -import com.amazonaws.services.s3.AmazonS3Client;  
12 -import com.amazonaws.services.s3.AmazonS3ClientBuilder;  
13 -import com.amazonaws.services.s3.model.ObjectMetadata;  
14 -import com.amazonaws.services.s3.model.S3Object;  
15 -  
16 -import java.io.ByteArrayInputStream;  
17 -import java.util.Date;  
18 -import java.util.concurrent.TimeUnit; 7 +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
  8 +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
  9 +import software.amazon.awssdk.core.sync.RequestBody;
  10 +import software.amazon.awssdk.regions.Region;
  11 +import software.amazon.awssdk.services.s3.S3Client;
  12 +import software.amazon.awssdk.services.s3.S3Configuration;
  13 +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
  14 +import software.amazon.awssdk.services.s3.model.GetObjectRequest;
  15 +import software.amazon.awssdk.services.s3.model.PutObjectRequest;
  16 +import software.amazon.awssdk.services.s3.presigner.S3Presigner;
  17 +import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
  18 +
  19 +import java.net.URI;
  20 +import java.time.Duration;
19 21
20 /** 22 /**
21 * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务 23 * 基于 S3 协议的文件客户端,实现 MinIO、阿里云、腾讯云、七牛云、华为云等云服务
22 - * <p>  
23 - * S3 协议的客户端,采用亚马逊提供的 software.amazon.awssdk.s3 库  
24 * 24 *
25 * @author 芋道源码 25 * @author 芋道源码
26 */ 26 */
27 public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { 27 public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
28 28
29 - private AmazonS3Client client; 29 + private S3Client client;
30 30
31 public S3FileClient(Long id, S3FileClientConfig config) { 31 public S3FileClient(Long id, S3FileClientConfig config) {
32 super(id, config); 32 super(id, config);
33 } 33 }
34 34
35 - @Override  
36 - protected void doInit() {  
37 - // 补全 domain  
38 - if (StrUtil.isEmpty(config.getDomain())) {  
39 - config.setDomain(buildDomain());  
40 - }  
41 - // 初始化客户端  
42 - client = (AmazonS3Client)AmazonS3ClientBuilder.standard()  
43 - .withCredentials(buildCredentials())  
44 - .withEndpointConfiguration(buildEndpointConfiguration())  
45 - .build();  
46 - }  
47 -  
48 /** 35 /**
49 - * 基于 config 秘钥,构建 S3 客户端的认证信息 36 + * 动态创建 S3Presigner
50 * 37 *
51 - * @return S3 客户端的认证信息 38 + * @param endpoint 节点地址
  39 + * @param accessKey 访问 Key
  40 + * @param secretKey 访问 Secret
  41 + * @return S3Presigner
52 */ 42 */
53 - private AWSStaticCredentialsProvider buildCredentials() {  
54 - return new AWSStaticCredentialsProvider(  
55 - new BasicAWSCredentials(config.getAccessKey(), config.getAccessSecret())); 43 + private static S3Presigner createPresigner(String endpoint, String accessKey, String secretKey) {
  44 + return S3Presigner.builder()
  45 + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey)))
  46 + .endpointOverride(URI.create(endpoint))
  47 + .build();
56 } 48 }
57 49
58 /** 50 /**
59 - * 构建 S3 客户端的 Endpoint 配置,包括 region、endpoint 51 + * 生成动态的预签名上传 URL
60 * 52 *
61 - * @return S3 客户端的 EndpointConfiguration 配置 53 + * @param bucket 存储 Bucket
  54 + * @param path 相对路径
  55 + * @param duration 过期时间
  56 + * @param endpoint 节点地址
  57 + * @param accessKey 访问 Key
  58 + * @param secretKey 访问 Secret
  59 + * @return 生成的上传 URL
62 */ 60 */
63 - private AwsClientBuilder.EndpointConfiguration buildEndpointConfiguration() {  
64 - return new AwsClientBuilder.EndpointConfiguration(config.getEndpoint(),  
65 - null); // 无需设置 region 61 + public static String getPresignedUrl(String bucket, String path, Duration duration,
  62 + String endpoint, String accessKey, String secretKey) {
  63 + try (S3Presigner presigner = createPresigner(endpoint, accessKey, secretKey)) {
  64 + return presigner.presignPutObject(PutObjectPresignRequest.builder()
  65 + .signatureDuration(duration)
  66 + .putObjectRequest(b -> b.bucket(bucket).key(path))
  67 + .build()).url().toString();
  68 + }
66 } 69 }
67 70
68 /** 71 /**
@@ -80,39 +83,90 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; { @@ -80,39 +83,90 @@ public class S3FileClient extends AbstractFileClient&lt;S3FileClientConfig&gt; {
80 } 83 }
81 84
82 @Override 85 @Override
83 - public String upload(byte[] content, String path, String type) throws Exception {  
84 - // 元数据,主要用于设置文件类型  
85 - ObjectMetadata objectMetadata = new ObjectMetadata();  
86 - objectMetadata.setContentType(type);  
87 - objectMetadata.setContentLength(content.length); // 如果不设置,会有 “ No content length specified for stream data” 警告日志  
88 - // 执行上传  
89 - client.putObject(config.getBucket(),  
90 - path, // 相对路径  
91 - new ByteArrayInputStream(content), // 文件内容  
92 - objectMetadata); 86 + protected void doInit() {
  87 + // 补全 domain
  88 + if (StrUtil.isEmpty(config.getDomain())) {
  89 + config.setDomain(buildDomain());
  90 + }
  91 + // 初始化 S3 客户端
  92 + client = S3Client.builder()
  93 + .credentialsProvider(buildCredentials())
  94 + .region(Region.of(config.getEndpoint())) // 这里随便填,SDK 需要
  95 + .endpointOverride(URI.create(buildEndpoint()))
  96 + .serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build()) // Path-style 访问
  97 + .build();
  98 + }
  99 +
  100 + /**
  101 + * 基于 config 秘钥,构建 S3 客户端的认证信息
  102 + *
  103 + * @return S3 客户端的认证信息
  104 + */
  105 + private StaticCredentialsProvider buildCredentials() {
  106 + return StaticCredentialsProvider.create(
  107 + AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret()));
  108 + }
  109 +
  110 + /**
  111 + * 节点地址补全协议头
  112 + *
  113 + * @return 节点地址
  114 + */
  115 + private String buildEndpoint() {
  116 + // 如果已经是 http 或者 https,则不进行拼接
  117 + if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
  118 + return config.getEndpoint();
  119 + }
  120 + return StrUtil.format("https://{}", config.getEndpoint());
  121 + }
  122 +
  123 + @Override
  124 + public String upload(byte[] content, String path, String type) {
  125 + // 构造 PutObjectRequest
  126 + PutObjectRequest putRequest = PutObjectRequest.builder()
  127 + .bucket(config.getBucket())
  128 + .key(path)
  129 + .contentType(type)
  130 + .contentLength((long) content.length)
  131 + .build();
93 132
  133 + // 上传文件
  134 + client.putObject(putRequest, RequestBody.fromBytes(content));
94 // 拼接返回路径 135 // 拼接返回路径
95 return config.getDomain() + "/" + path; 136 return config.getDomain() + "/" + path;
96 } 137 }
97 138
98 @Override 139 @Override
99 - public void delete(String path) throws Exception {  
100 - client.deleteObject(config.getBucket(), path); 140 + public void delete(String path) {
  141 + DeleteObjectRequest deleteRequest = DeleteObjectRequest.builder()
  142 + .bucket(config.getBucket())
  143 + .key(path)
  144 + .build();
  145 + client.deleteObject(deleteRequest);
101 } 146 }
102 147
103 @Override 148 @Override
104 - public byte[] getContent(String path) throws Exception {  
105 - S3Object tempS3Object = client.getObject(config.getBucket(), path);  
106 - return IoUtil.readBytes(tempS3Object.getObjectContent()); 149 + public byte[] getContent(String path) {
  150 + GetObjectRequest getRequest = GetObjectRequest.builder()
  151 + .bucket(config.getBucket())
  152 + .key(path)
  153 + .build();
  154 +
  155 + return IoUtil.readBytes(client.getObject(getRequest));
107 } 156 }
108 157
109 @Override 158 @Override
110 - public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception {  
111 - // 设定过期时间为 10 分钟。取值范围:1 秒 ~ 7 天  
112 - Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10));  
113 - // 生成上传 URL  
114 - String uploadUrl = String.valueOf(client.generatePresignedUrl(config.getBucket(), path, expiration , HttpMethod.PUT));  
115 - return new FilePresignedUrlRespDTO(uploadUrl, config.getDomain() + "/" + path); 159 + public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) {
  160 + String presignedUrl = getPresignedUrl(
  161 + config.getBucket(),
  162 + path,
  163 + Duration.ofMinutes(10),
  164 + config.getEndpoint(),
  165 + config.getAccessKey(),
  166 + config.getAccessSecret()
  167 + );
  168 +
  169 + return new FilePresignedUrlRespDTO(presignedUrl, config.getDomain() + "/" + path);
116 } 170 }
117 171
118 } 172 }