Commit 46676a439d22477b9e240c81614cdc594c2dbb94
1 parent
0885eb7c
feat:【INFRA】文件存储,增加 enablePathStyleAccess 选项
Showing
4 changed files
with
34 additions
and
49 deletions
yudao-dependencies/pom.xml
| @@ -96,10 +96,8 @@ | @@ -96,10 +96,8 @@ | ||
| 96 | </dependency> | 96 | </dependency> |
| 97 | <dependency> | 97 | <dependency> |
| 98 | <groupId>software.amazon.awssdk</groupId> | 98 | <groupId>software.amazon.awssdk</groupId> |
| 99 | - <artifactId>bom</artifactId> | 99 | + <artifactId>s3</artifactId> |
| 100 | <version>${awssdk.version}</version> | 100 | <version>${awssdk.version}</version> |
| 101 | - <type>pom</type> | ||
| 102 | - <scope>import</scope> | ||
| 103 | </dependency> | 101 | </dependency> |
| 104 | 102 | ||
| 105 | <!-- 业务组件 --> | 103 | <!-- 业务组件 --> |
yudao-module-infra/yudao-module-infra-biz/pom.xml
| @@ -120,14 +120,6 @@ | @@ -120,14 +120,6 @@ | ||
| 120 | <groupId>software.amazon.awssdk</groupId> | 120 | <groupId>software.amazon.awssdk</groupId> |
| 121 | <artifactId>s3</artifactId> | 121 | <artifactId>s3</artifactId> |
| 122 | </dependency> | 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> | ||
| 130 | - </dependency> | ||
| 131 | 123 | ||
| 132 | <dependency> | 124 | <dependency> |
| 133 | <groupId>org.apache.tika</groupId> | 125 | <groupId>org.apache.tika</groupId> |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java
| @@ -5,10 +5,12 @@ import cn.hutool.core.util.StrUtil; | @@ -5,10 +5,12 @@ 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 software.amazon.awssdk.auth.credentials.AwsBasicCredentials; | 7 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; |
| 8 | +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; | ||
| 8 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; | 9 | import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; |
| 9 | import software.amazon.awssdk.core.sync.RequestBody; | 10 | import software.amazon.awssdk.core.sync.RequestBody; |
| 10 | import software.amazon.awssdk.regions.Region; | 11 | import software.amazon.awssdk.regions.Region; |
| 11 | import software.amazon.awssdk.services.s3.S3Client; | 12 | import software.amazon.awssdk.services.s3.S3Client; |
| 13 | +import software.amazon.awssdk.services.s3.S3Configuration; | ||
| 12 | import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; | 14 | import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; |
| 13 | import software.amazon.awssdk.services.s3.model.GetObjectRequest; | 15 | import software.amazon.awssdk.services.s3.model.GetObjectRequest; |
| 14 | import software.amazon.awssdk.services.s3.model.PutObjectRequest; | 16 | import software.amazon.awssdk.services.s3.model.PutObjectRequest; |
| @@ -26,6 +28,7 @@ import java.time.Duration; | @@ -26,6 +28,7 @@ import java.time.Duration; | ||
| 26 | public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | 28 | public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { |
| 27 | 29 | ||
| 28 | private S3Client client; | 30 | private S3Client client; |
| 31 | + private S3Presigner presigner; | ||
| 29 | 32 | ||
| 30 | public S3FileClient(Long id, S3FileClientConfig config) { | 33 | public S3FileClient(Long id, S3FileClientConfig config) { |
| 31 | super(id, config); | 34 | super(id, config); |
| @@ -38,11 +41,23 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | @@ -38,11 +41,23 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | ||
| 38 | config.setDomain(buildDomain()); | 41 | config.setDomain(buildDomain()); |
| 39 | } | 42 | } |
| 40 | // 初始化 S3 客户端 | 43 | // 初始化 S3 客户端 |
| 44 | + Region region = Region.of("us-east-1"); // 必须填,但填什么都行,常见的值有 "us-east-1",不填会报错 | ||
| 45 | + AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create( | ||
| 46 | + AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret())); | ||
| 47 | + URI endpoint = URI.create(buildEndpoint()); | ||
| 48 | + S3Configuration serviceConfiguration = S3Configuration.builder() // Path-style 访问 | ||
| 49 | + .pathStyleAccessEnabled(Boolean.TRUE.equals(config.getEnablePathStyleAccess())).build(); | ||
| 41 | client = S3Client.builder() | 50 | client = S3Client.builder() |
| 42 | - .credentialsProvider(buildCredentials()) | ||
| 43 | - .region(Region.of("us-east-1")) // 必须填,但填什么都行,常见的值有 "us-east-1",不填会报错 | ||
| 44 | - .endpointOverride(URI.create(buildEndpoint())) | ||
| 45 | - //.serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build()) // Path-style 访问 | 51 | + .credentialsProvider(credentialsProvider) |
| 52 | + .region(region) | ||
| 53 | + .endpointOverride(endpoint) | ||
| 54 | + .serviceConfiguration(serviceConfiguration) | ||
| 55 | + .build(); | ||
| 56 | + presigner = S3Presigner.builder() | ||
| 57 | + .credentialsProvider(credentialsProvider) | ||
| 58 | + .region(region) | ||
| 59 | + .endpointOverride(endpoint) | ||
| 60 | + .serviceConfiguration(serviceConfiguration) | ||
| 46 | .build(); | 61 | .build(); |
| 47 | } | 62 | } |
| 48 | 63 | ||
| @@ -55,7 +70,6 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | @@ -55,7 +70,6 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | ||
| 55 | .contentType(type) | 70 | .contentType(type) |
| 56 | .contentLength((long) content.length) | 71 | .contentLength((long) content.length) |
| 57 | .build(); | 72 | .build(); |
| 58 | - | ||
| 59 | // 上传文件 | 73 | // 上传文件 |
| 60 | client.putObject(putRequest, RequestBody.fromBytes(content)); | 74 | client.putObject(putRequest, RequestBody.fromBytes(content)); |
| 61 | // 拼接返回路径 | 75 | // 拼接返回路径 |
| @@ -77,42 +91,27 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | @@ -77,42 +91,27 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | ||
| 77 | .bucket(config.getBucket()) | 91 | .bucket(config.getBucket()) |
| 78 | .key(path) | 92 | .key(path) |
| 79 | .build(); | 93 | .build(); |
| 80 | - | ||
| 81 | return IoUtil.readBytes(client.getObject(getRequest)); | 94 | return IoUtil.readBytes(client.getObject(getRequest)); |
| 82 | } | 95 | } |
| 83 | 96 | ||
| 84 | @Override | 97 | @Override |
| 85 | public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) { | 98 | public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) { |
| 86 | - return new FilePresignedUrlRespDTO(getPresignedUrl(path, Duration.ofMinutes(10)), config.getDomain() + "/" + path); | ||
| 87 | - } | ||
| 88 | - | ||
| 89 | - /** | ||
| 90 | - * 动态创建 S3Presigner | ||
| 91 | - * | ||
| 92 | - * @return S3Presigner | ||
| 93 | - */ | ||
| 94 | - private S3Presigner createPresigner() { | ||
| 95 | - return S3Presigner.builder() | ||
| 96 | - .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret()))) | ||
| 97 | - .region(Region.of("us-east-1")) // 必须填,但填什么都行,常见的值有 "us-east-1",不填会报错 | ||
| 98 | - .endpointOverride(URI.create(buildEndpoint())) | ||
| 99 | - .build(); | 99 | + Duration expiration = Duration.ofHours(24); |
| 100 | + return new FilePresignedUrlRespDTO(getPresignedUrl(path, expiration), config.getDomain() + "/" + path); | ||
| 100 | } | 101 | } |
| 101 | 102 | ||
| 102 | /** | 103 | /** |
| 103 | * 生成动态的预签名上传 URL | 104 | * 生成动态的预签名上传 URL |
| 104 | * | 105 | * |
| 105 | * @param path 相对路径 | 106 | * @param path 相对路径 |
| 106 | - * @param duration 过期时间 | 107 | + * @param expiration 过期时间 |
| 107 | * @return 生成的上传 URL | 108 | * @return 生成的上传 URL |
| 108 | */ | 109 | */ |
| 109 | - public String getPresignedUrl(String path, Duration duration) { | ||
| 110 | - try (S3Presigner presigner = createPresigner()) { | ||
| 111 | - return presigner.presignPutObject(PutObjectPresignRequest.builder() | ||
| 112 | - .signatureDuration(duration) | ||
| 113 | - .putObjectRequest(b -> b.bucket(config.getBucket()).key(path)) | ||
| 114 | - .build()).url().toString(); | ||
| 115 | - } | 110 | + private String getPresignedUrl(String path, Duration expiration) { |
| 111 | + return presigner.presignPutObject(PutObjectPresignRequest.builder() | ||
| 112 | + .signatureDuration(expiration) | ||
| 113 | + .putObjectRequest(b -> b.bucket(config.getBucket()).key(path)) | ||
| 114 | + .build()).url().toString(); | ||
| 116 | } | 115 | } |
| 117 | 116 | ||
| 118 | /** | 117 | /** |
| @@ -130,16 +129,6 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | @@ -130,16 +129,6 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { | ||
| 130 | } | 129 | } |
| 131 | 130 | ||
| 132 | /** | 131 | /** |
| 133 | - * 基于 config 秘钥,构建 S3 客户端的认证信息 | ||
| 134 | - * | ||
| 135 | - * @return S3 客户端的认证信息 | ||
| 136 | - */ | ||
| 137 | - private StaticCredentialsProvider buildCredentials() { | ||
| 138 | - return StaticCredentialsProvider.create( | ||
| 139 | - AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret())); | ||
| 140 | - } | ||
| 141 | - | ||
| 142 | - /** | ||
| 143 | * 节点地址补全协议头 | 132 | * 节点地址补全协议头 |
| 144 | * | 133 | * |
| 145 | * @return 节点地址 | 134 | * @return 节点地址 |
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClientConfig.java
| @@ -67,6 +67,12 @@ public class S3FileClientConfig implements FileClientConfig { | @@ -67,6 +67,12 @@ public class S3FileClientConfig implements FileClientConfig { | ||
| 67 | @NotNull(message = "accessSecret 不能为空") | 67 | @NotNull(message = "accessSecret 不能为空") |
| 68 | private String accessSecret; | 68 | private String accessSecret; |
| 69 | 69 | ||
| 70 | + /** | ||
| 71 | + * 是否启用 PathStyle 访问 | ||
| 72 | + */ | ||
| 73 | + @NotNull(message = "enablePathStyleAccess 不能为空") | ||
| 74 | + private Boolean enablePathStyleAccess; | ||
| 75 | + | ||
| 70 | @SuppressWarnings("RedundantIfStatement") | 76 | @SuppressWarnings("RedundantIfStatement") |
| 71 | @AssertTrue(message = "domain 不能为空") | 77 | @AssertTrue(message = "domain 不能为空") |
| 72 | @JsonIgnore | 78 | @JsonIgnore |