Merge branch 'master-jdk17' into feature-project

This commit is contained in:
2024-11-18 08:56:19 +08:00
11 changed files with 241 additions and 117 deletions

View File

@ -116,8 +116,8 @@
<artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 -->
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId> <!-- 文件客户端解决阿里云、腾讯云、minio 等 S3 连接 -->
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId><!-- 文件客户端解决阿里云、腾讯云、minio 等 S3 连接 -->
</dependency>
<dependency>

View File

@ -4,10 +4,17 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.module.infra.framework.file.core.client.AbstractFileClient;
import io.minio.*;
import io.minio.http.Method;
import com.amazonaws.HttpMethod;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.S3Object;
import java.io.ByteArrayInputStream;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
@ -19,7 +26,7 @@ import java.util.concurrent.TimeUnit;
*/
public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
private MinioClient client;
private AmazonS3Client client;
public S3FileClient(Long id, S3FileClientConfig config) {
super(id, config);
@ -32,25 +39,30 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
config.setDomain(buildDomain());
}
// 初始化客户端
client = MinioClient.builder()
.endpoint(buildEndpointURL()) // Endpoint URL
.region(buildRegion()) // Region
.credentials(config.getAccessKey(), config.getAccessSecret()) // 认证密钥
client = (AmazonS3Client)AmazonS3ClientBuilder.standard()
.withCredentials(buildCredentials())
.withEndpointConfiguration(buildEndpointConfiguration())
.build();
enableVirtualStyleEndpoint();
}
/**
* 基于 endpoint 构建调用云服务的 URL 地址
* 基于 config 秘钥,构建 S3 客户端的认证信息
*
* @return URI 地址
* @return S3 客户端的认证信息
*/
private String buildEndpointURL() {
// 如果已经是 http 或者 https则不进行拼接.主要适配 MinIO
if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
return config.getEndpoint();
}
return StrUtil.format("https://{}", config.getEndpoint());
private AWSStaticCredentialsProvider buildCredentials() {
return new AWSStaticCredentialsProvider(
new BasicAWSCredentials(config.getAccessKey(), config.getAccessSecret()));
}
/**
* 构建 S3 客户端的 Endpoint 配置,包括 region、endpoint
*
* @return S3 客户端的 EndpointConfiguration 配置
*/
private AwsClientBuilder.EndpointConfiguration buildEndpointConfiguration() {
return new AwsClientBuilder.EndpointConfiguration(config.getEndpoint(),
null); // 无需设置 region
}
/**
@ -67,76 +79,39 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint());
}
/**
* 基于 bucket 构建 region 地区
*
* @return region 地区
*/
private String buildRegion() {
// 阿里云必须有 region否则会报错
if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_ALIYUN)) {
return StrUtil.subBefore(config.getEndpoint(), '.', false)
.replaceAll("-internal", "")// 去除内网 Endpoint 的后缀
.replaceAll("https://", "");
}
// 腾讯云必须有 region否则会报错
if (config.getEndpoint().contains(S3FileClientConfig.ENDPOINT_TENCENT)) {
return StrUtil.subAfter(config.getEndpoint(), "cos.", false)
.replaceAll("." + S3FileClientConfig.ENDPOINT_TENCENT, ""); // 去除 Endpoint
}
return null;
}
/**
* 开启 VirtualStyle 模式
*/
private void enableVirtualStyleEndpoint() {
if (StrUtil.containsAny(config.getEndpoint(),
S3FileClientConfig.ENDPOINT_TENCENT, // 腾讯云 https://cloud.tencent.com/document/product/436/41284
S3FileClientConfig.ENDPOINT_VOLCES)) { // 火山云 https://www.volcengine.com/docs/6349/1288493
client.enableVirtualStyleEndpoint();
}
}
@Override
public String upload(byte[] content, String path, String type) throws Exception {
// 元数据,主要用于设置文件类型
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentType(type);
objectMetadata.setContentLength(content.length); // 如果不设置,会有 “ No content length specified for stream data” 警告日志
// 执行上传
client.putObject(PutObjectArgs.builder()
.bucket(config.getBucket()) // bucket 必须传递
.contentType(type)
.object(path) // 相对路径作为 key
.stream(new ByteArrayInputStream(content), content.length, -1) // 文件内容
.build());
client.putObject(config.getBucket(),
path, // 相对路径
new ByteArrayInputStream(content), // 文件内容
objectMetadata);
// 拼接返回路径
return config.getDomain() + "/" + path;
}
@Override
public void delete(String path) throws Exception {
client.removeObject(RemoveObjectArgs.builder()
.bucket(config.getBucket()) // bucket 必须传递
.object(path) // 相对路径作为 key
.build());
client.deleteObject(config.getBucket(), path);
}
@Override
public byte[] getContent(String path) throws Exception {
GetObjectResponse response = client.getObject(GetObjectArgs.builder()
.bucket(config.getBucket()) // bucket 必须传递
.object(path) // 相对路径作为 key
.build());
return IoUtil.readBytes(response);
S3Object tempS3Object = client.getObject(config.getBucket(), path);
return IoUtil.readBytes(tempS3Object.getObjectContent());
}
@Override
public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) throws Exception {
String uploadUrl = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.PUT)
.bucket(config.getBucket())
.object(path)
.expiry(10, TimeUnit.MINUTES) // 过期时间秒数取值范围1 秒 ~ 7 天
.build()
);
// 设定过期时间为 10 分钟。取值范围1 秒 ~ 7 天
Date expiration = new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(10));
// 生成上传 URL
String uploadUrl = String.valueOf(client.generatePresignedUrl(config.getBucket(), path, expiration , HttpMethod.PUT));
return new FilePresignedUrlRespDTO(uploadUrl, config.getDomain() + "/" + path);
}

View File

@ -28,7 +28,8 @@ public class S3FileClientConfig implements FileClientConfig {
* 2. 阿里云https://help.aliyun.com/document_detail/31837.html
* 3. 腾讯云https://cloud.tencent.com/document/product/436/6224
* 4. 七牛云https://developer.qiniu.com/kodo/4088/s3-access-domainname
* 5. 华为云https://developer.huaweicloud.com/endpoint?OBS
* 5. 华为云https://console.huaweicloud.com/apiexplorer/#/endpoint/OBS
* 6. 火山云https://www.volcengine.com/docs/6349/107356
*/
@NotNull(message = "endpoint 不能为空")
private String endpoint;
@ -39,6 +40,7 @@ public class S3FileClientConfig implements FileClientConfig {
* 3. 腾讯云https://cloud.tencent.com/document/product/436/11142
* 4. 七牛云https://developer.qiniu.com/kodo/8556/set-the-custom-source-domain-name
* 5. 华为云https://support.huaweicloud.com/usermanual-obs/obs_03_0032.html
* 6. 火山云https://www.volcengine.com/docs/6349/128983
*/
@URL(message = "domain 必须是 URL 格式")
private String domain;
@ -55,6 +57,7 @@ public class S3FileClientConfig implements FileClientConfig {
* 3. 腾讯云https://console.cloud.tencent.com/cam/capi
* 4. 七牛云https://portal.qiniu.com/user/key
* 5. 华为云https://support.huaweicloud.com/qs-obs/obs_qs_0005.html
* 6. 火山云https://console.volcengine.com/iam/keymanage/
*/
@NotNull(message = "accessKey 不能为空")
private String accessKey;