Commit f141d64eb2bd755a44564157d3c5254506230d11
1 parent
d83b7cd5
【依赖升级】AWS SDK for Java 1.x to 2.x
Showing
3 changed files
with
132 additions
and
69 deletions
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<S3FileClientConfig> { | @@ -80,39 +83,90 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | ||
| 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 | } |