feat:【INFRA】文件存储,增加 enablePathStyleAccess 选项

This commit is contained in:
YunaiV 2025-04-25 21:48:37 +08:00
parent 0885eb7c70
commit 46676a439d
4 changed files with 34 additions and 49 deletions

View File

@ -96,10 +96,8 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>software.amazon.awssdk</groupId> <groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId> <artifactId>s3</artifactId>
<version>${awssdk.version}</version> <version>${awssdk.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency> </dependency>
<!-- 业务组件 --> <!-- 业务组件 -->

View File

@ -120,14 +120,6 @@
<groupId>software.amazon.awssdk</groupId> <groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId> <artifactId>s3</artifactId>
</dependency> </dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>auth</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>regions</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.apache.tika</groupId> <groupId>org.apache.tika</groupId>

View File

@ -5,10 +5,12 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient; import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest;
@ -26,6 +28,7 @@ import java.time.Duration;
public class S3FileClient extends AbstractFileClient<S3FileClientConfig> { public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
private S3Client client; private S3Client client;
private S3Presigner presigner;
public S3FileClient(Long id, S3FileClientConfig config) { public S3FileClient(Long id, S3FileClientConfig config) {
super(id, config); super(id, config);
@ -38,11 +41,23 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
config.setDomain(buildDomain()); config.setDomain(buildDomain());
} }
// 初始化 S3 客户端 // 初始化 S3 客户端
Region region = Region.of("us-east-1"); // 必须填但填什么都行常见的值有 "us-east-1"不填会报错
AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret()));
URI endpoint = URI.create(buildEndpoint());
S3Configuration serviceConfiguration = S3Configuration.builder() // Path-style 访问
.pathStyleAccessEnabled(Boolean.TRUE.equals(config.getEnablePathStyleAccess())).build();
client = S3Client.builder() client = S3Client.builder()
.credentialsProvider(buildCredentials()) .credentialsProvider(credentialsProvider)
.region(Region.of("us-east-1")) // 必须填但填什么都行常见的值有 "us-east-1"不填会报错 .region(region)
.endpointOverride(URI.create(buildEndpoint())) .endpointOverride(endpoint)
//.serviceConfiguration(S3Configuration.builder().pathStyleAccessEnabled(true).build()) // Path-style 访问 .serviceConfiguration(serviceConfiguration)
.build();
presigner = S3Presigner.builder()
.credentialsProvider(credentialsProvider)
.region(region)
.endpointOverride(endpoint)
.serviceConfiguration(serviceConfiguration)
.build(); .build();
} }
@ -55,7 +70,6 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
.contentType(type) .contentType(type)
.contentLength((long) content.length) .contentLength((long) content.length)
.build(); .build();
// 上传文件 // 上传文件
client.putObject(putRequest, RequestBody.fromBytes(content)); client.putObject(putRequest, RequestBody.fromBytes(content));
// 拼接返回路径 // 拼接返回路径
@ -77,42 +91,27 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
.bucket(config.getBucket()) .bucket(config.getBucket())
.key(path) .key(path)
.build(); .build();
return IoUtil.readBytes(client.getObject(getRequest)); return IoUtil.readBytes(client.getObject(getRequest));
} }
@Override @Override
public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) { public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) {
return new FilePresignedUrlRespDTO(getPresignedUrl(path, Duration.ofMinutes(10)), config.getDomain() + "/" + path); Duration expiration = Duration.ofHours(24);
} return new FilePresignedUrlRespDTO(getPresignedUrl(path, expiration), config.getDomain() + "/" + path);
/**
* 动态创建 S3Presigner
*
* @return S3Presigner
*/
private S3Presigner createPresigner() {
return S3Presigner.builder()
.credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret())))
.region(Region.of("us-east-1")) // 必须填但填什么都行常见的值有 "us-east-1"不填会报错
.endpointOverride(URI.create(buildEndpoint()))
.build();
} }
/** /**
* 生成动态的预签名上传 URL * 生成动态的预签名上传 URL
* *
* @param path 相对路径 * @param path 相对路径
* @param duration 过期时间 * @param expiration 过期时间
* @return 生成的上传 URL * @return 生成的上传 URL
*/ */
public String getPresignedUrl(String path, Duration duration) { private String getPresignedUrl(String path, Duration expiration) {
try (S3Presigner presigner = createPresigner()) { return presigner.presignPutObject(PutObjectPresignRequest.builder()
return presigner.presignPutObject(PutObjectPresignRequest.builder() .signatureDuration(expiration)
.signatureDuration(duration) .putObjectRequest(b -> b.bucket(config.getBucket()).key(path))
.putObjectRequest(b -> b.bucket(config.getBucket()).key(path)) .build()).url().toString();
.build()).url().toString();
}
} }
/** /**
@ -129,16 +128,6 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint()); return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint());
} }
/**
* 基于 config 秘钥构建 S3 客户端的认证信息
*
* @return S3 客户端的认证信息
*/
private StaticCredentialsProvider buildCredentials() {
return StaticCredentialsProvider.create(
AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret()));
}
/** /**
* 节点地址补全协议头 * 节点地址补全协议头
* *

View File

@ -67,6 +67,12 @@ public class S3FileClientConfig implements FileClientConfig {
@NotNull(message = "accessSecret 不能为空") @NotNull(message = "accessSecret 不能为空")
private String accessSecret; private String accessSecret;
/**
* 是否启用 PathStyle 访问
*/
@NotNull(message = "enablePathStyleAccess 不能为空")
private Boolean enablePathStyleAccess;
@SuppressWarnings("RedundantIfStatement") @SuppressWarnings("RedundantIfStatement")
@AssertTrue(message = "domain 不能为空") @AssertTrue(message = "domain 不能为空")
@JsonIgnore @JsonIgnore