|
@@ -41,6 +41,7 @@ import java.nio.file.Path;
|
|
|
import java.text.SimpleDateFormat;
|
|
import java.text.SimpleDateFormat;
|
|
|
import java.time.Duration;
|
|
import java.time.Duration;
|
|
|
import java.util.Date;
|
|
import java.util.Date;
|
|
|
|
|
+import java.util.Map;
|
|
|
import java.util.Optional;
|
|
import java.util.Optional;
|
|
|
import java.util.Random;
|
|
import java.util.Random;
|
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Consumer;
|
|
@@ -79,13 +80,13 @@ public class OssClient {
|
|
|
S3TransferManager transferManager = null;
|
|
S3TransferManager transferManager = null;
|
|
|
S3Presigner presigner = null;
|
|
S3Presigner presigner = null;
|
|
|
try {
|
|
try {
|
|
|
-// if (isLocal() && StringUtils.isEmpty(ossProperties.getEndpoint())) {
|
|
|
|
|
-// properties.setEndpoint("127.0.0.1:8080");
|
|
|
|
|
-// }
|
|
|
|
|
|
|
+ // if (isLocal() && StringUtils.isEmpty(ossProperties.getEndpoint())) {
|
|
|
|
|
+ // properties.setEndpoint("127.0.0.1:8080");
|
|
|
|
|
+ // }
|
|
|
if (!isLocal()) {
|
|
if (!isLocal()) {
|
|
|
StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
|
|
StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
|
|
|
AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey()));
|
|
AwsBasicCredentials.create(properties.getAccessKey(), properties.getSecretKey()));
|
|
|
- //MinIO 使用 HTTPS 限制使用域名访问,站点填域名。需要启用路径样式访问
|
|
|
|
|
|
|
+ // MinIO 使用 HTTPS 限制使用域名访问,站点填域名。需要启用路径样式访问
|
|
|
boolean isStyle = !StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE);
|
|
boolean isStyle = !StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE);
|
|
|
// 创建AWS基于 CRT 的 S3 客户端
|
|
// 创建AWS基于 CRT 的 S3 客户端
|
|
|
// 创建AWS基于 Netty 的 S3 客户端
|
|
// 创建AWS基于 Netty 的 S3 客户端
|
|
@@ -95,9 +96,13 @@ public class OssClient {
|
|
|
.region(of())
|
|
.region(of())
|
|
|
.forcePathStyle(isStyle)
|
|
.forcePathStyle(isStyle)
|
|
|
.httpClient(NettyNioAsyncHttpClient.builder()
|
|
.httpClient(NettyNioAsyncHttpClient.builder()
|
|
|
- .connectionTimeout(Duration.ofSeconds(60)).build())
|
|
|
|
|
|
|
+ .connectionTimeout(Duration.ofSeconds(60))
|
|
|
|
|
+ .connectionAcquisitionTimeout(Duration.ofSeconds(30))
|
|
|
|
|
+ .maxConcurrency(100)
|
|
|
|
|
+ .maxPendingConnectionAcquires(1000)
|
|
|
|
|
+ .build())
|
|
|
.build();
|
|
.build();
|
|
|
- //AWS基于 CRT 的 S3 AsyncClient 实例用作 S3 传输管理器的底层客户端
|
|
|
|
|
|
|
+ // AWS基于 CRT 的 S3 AsyncClient 实例用作 S3 传输管理器的底层客户端
|
|
|
transferManager = S3TransferManager.builder().s3Client(client).build();
|
|
transferManager = S3TransferManager.builder().s3Client(client).build();
|
|
|
|
|
|
|
|
// 创建 S3 配置对象
|
|
// 创建 S3 配置对象
|
|
@@ -158,14 +163,14 @@ public class OssClient {
|
|
|
// 构建上传请求对象
|
|
// 构建上传请求对象
|
|
|
FileUpload fileUpload = transferManager.uploadFile(
|
|
FileUpload fileUpload = transferManager.uploadFile(
|
|
|
x -> x.putObjectRequest(
|
|
x -> x.putObjectRequest(
|
|
|
- y -> y.bucket(properties.getBucketName())
|
|
|
|
|
- .key(key)
|
|
|
|
|
- .contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null)
|
|
|
|
|
- .contentType(contentType)
|
|
|
|
|
- // 用于设置对象的访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
|
|
|
|
|
- // 因此根据具体的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
|
|
|
|
|
- //.acl(getAccessPolicy().getObjectCannedACL())
|
|
|
|
|
- .build())
|
|
|
|
|
|
|
+ y -> y.bucket(properties.getBucketName())
|
|
|
|
|
+ .key(key)
|
|
|
|
|
+ .contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null)
|
|
|
|
|
+ .contentType(contentType)
|
|
|
|
|
+ // 用于设置对象的访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
|
|
|
|
|
+ // 因此根据具体的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
|
|
|
|
|
+ // .acl(getAccessPolicy().getObjectCannedACL())
|
|
|
|
|
+ .build())
|
|
|
.addTransferListener(LoggingTransferListener.create())
|
|
.addTransferListener(LoggingTransferListener.create())
|
|
|
.source(filePath).build());
|
|
.source(filePath).build());
|
|
|
|
|
|
|
@@ -223,7 +228,7 @@ public class OssClient {
|
|
|
.contentType(contentType)
|
|
.contentType(contentType)
|
|
|
// 用于设置对象的访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
|
|
// 用于设置对象的访问控制列表(ACL)。不同云厂商对ACL的支持和实现方式有所不同,
|
|
|
// 因此根据具体的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
|
|
// 因此根据具体的云服务提供商,你可能需要进行不同的配置(自行开启,阿里云有acl权限配置,腾讯云没有acl权限配置)
|
|
|
- //.acl(getAccessPolicy().getObjectCannedACL())
|
|
|
|
|
|
|
+ // .acl(getAccessPolicy().getObjectCannedACL())
|
|
|
.build())
|
|
.build())
|
|
|
.build());
|
|
.build());
|
|
|
|
|
|
|
@@ -322,7 +327,8 @@ public class OssClient {
|
|
|
* @throws OssException 如果上传失败,抛出自定义异常
|
|
* @throws OssException 如果上传失败,抛出自定义异常
|
|
|
*/
|
|
*/
|
|
|
public UploadResult uploadSuffix(byte[] data, String suffix) {
|
|
public UploadResult uploadSuffix(byte[] data, String suffix) {
|
|
|
- return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Convert.toLong(data.length), FileUtils.getMimeType(suffix));
|
|
|
|
|
|
|
+ return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix),
|
|
|
|
|
+ Convert.toLong(data.length), FileUtils.getMimeType(suffix));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -351,7 +357,8 @@ public class OssClient {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
|
|
public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
|
|
|
- return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix), Convert.toLong(data.length), contentType);
|
|
|
|
|
|
|
+ return upload(new ByteArrayInputStream(data), getPath(properties.getPrefix(), suffix),
|
|
|
|
|
+ Convert.toLong(data.length), contentType);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -367,9 +374,9 @@ public class OssClient {
|
|
|
// 使用 S3TransferManager 下载文件
|
|
// 使用 S3TransferManager 下载文件
|
|
|
FileDownload downloadFile = transferManager.downloadFile(
|
|
FileDownload downloadFile = transferManager.downloadFile(
|
|
|
x -> x.getObjectRequest(
|
|
x -> x.getObjectRequest(
|
|
|
- y -> y.bucket(properties.getBucketName())
|
|
|
|
|
- .key(removeBaseUrl(path))
|
|
|
|
|
- .build())
|
|
|
|
|
|
|
+ y -> y.bucket(properties.getBucketName())
|
|
|
|
|
+ .key(removeBaseUrl(path))
|
|
|
|
|
+ .build())
|
|
|
.addTransferListener(LoggingTransferListener.create())
|
|
.addTransferListener(LoggingTransferListener.create())
|
|
|
.destination(tempFilePath)
|
|
.destination(tempFilePath)
|
|
|
.build());
|
|
.build());
|
|
@@ -415,7 +422,8 @@ public class OssClient {
|
|
|
.build();
|
|
.build();
|
|
|
|
|
|
|
|
// 使用 S3TransferManager 下载文件
|
|
// 使用 S3TransferManager 下载文件
|
|
|
- Download<ResponsePublisher<GetObjectResponse>> publisherDownload = transferManager.download(publisherDownloadRequest);
|
|
|
|
|
|
|
+ Download<ResponsePublisher<GetObjectResponse>> publisherDownload = transferManager
|
|
|
|
|
+ .download(publisherDownloadRequest);
|
|
|
// 获取下载发布订阅转换器
|
|
// 获取下载发布订阅转换器
|
|
|
ResponsePublisher<GetObjectResponse> publisher = publisherDownload.completionFuture().join().result();
|
|
ResponsePublisher<GetObjectResponse> publisher = publisherDownload.completionFuture().join().result();
|
|
|
// 执行文件大小消费者函数
|
|
// 执行文件大小消费者函数
|
|
@@ -527,7 +535,7 @@ public class OssClient {
|
|
|
* @return 对应的 AWS 区域对象,或者默认的广泛支持的区域(us-east-1)
|
|
* @return 对应的 AWS 区域对象,或者默认的广泛支持的区域(us-east-1)
|
|
|
*/
|
|
*/
|
|
|
public Region of() {
|
|
public Region of() {
|
|
|
- //AWS 区域字符串
|
|
|
|
|
|
|
+ // AWS 区域字符串
|
|
|
String region = properties.getRegion();
|
|
String region = properties.getRegion();
|
|
|
// 如果 region 参数非空,使用 Region.of 方法创建对应的 AWS 区域对象,否则返回默认区域
|
|
// 如果 region 参数非空,使用 Region.of 方法创建对应的 AWS 区域对象,否则返回默认区域
|
|
|
return StringUtils.isNotEmpty(region) ? Region.of(region) : Region.US_EAST_1;
|
|
return StringUtils.isNotEmpty(region) ? Region.of(region) : Region.US_EAST_1;
|
|
@@ -563,16 +571,16 @@ public class OssClient {
|
|
|
return path.replace(getUrl() + StringUtils.SLASH, "");
|
|
return path.replace(getUrl() + StringUtils.SLASH, "");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// /**
|
|
|
|
|
-// * 获取文件元数据
|
|
|
|
|
-// *
|
|
|
|
|
-// * @param path 完整文件路径
|
|
|
|
|
-// */
|
|
|
|
|
-// public ObjectMetadata getObjectMetadata(String path) {
|
|
|
|
|
-// path = path.replace(getUrl() + "/", "");
|
|
|
|
|
-// S3Object object = client.getObject(properties.getBucketName(), path);
|
|
|
|
|
-// return object.getObjectMetadata();
|
|
|
|
|
-// }
|
|
|
|
|
|
|
+ // /**
|
|
|
|
|
+ // * 获取文件元数据
|
|
|
|
|
+ // *
|
|
|
|
|
+ // * @param path 完整文件路径
|
|
|
|
|
+ // */
|
|
|
|
|
+ // public ObjectMetadata getObjectMetadata(String path) {
|
|
|
|
|
+ // path = path.replace(getUrl() + "/", "");
|
|
|
|
|
+ // S3Object object = client.getObject(properties.getBucketName(), path);
|
|
|
|
|
+ // return object.getObjectMetadata();
|
|
|
|
|
+ // }
|
|
|
|
|
|
|
|
public InputStream getObjectContent(String path) throws IOException {
|
|
public InputStream getObjectContent(String path) throws IOException {
|
|
|
// 下载文件到临时目录
|
|
// 下载文件到临时目录
|
|
@@ -600,18 +608,18 @@ public class OssClient {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// private InputStream getObjectContent(String fileCode, String path) {
|
|
|
|
|
-// String filePath = VbFile.getDirectory(path, fileCode);
|
|
|
|
|
-// VbFile vbFile = new VbFile(filePath, fileCode);
|
|
|
|
|
-// try {
|
|
|
|
|
-// try (InputStream stream = new FileInputStream(vbFile)) {
|
|
|
|
|
-// return stream;
|
|
|
|
|
-// }
|
|
|
|
|
-// } catch (Exception e) {
|
|
|
|
|
-// log.error("获取文件流异常: {}", e.getMessage());
|
|
|
|
|
-// throw new OssException("获取文件流异常: " + e.getMessage());
|
|
|
|
|
-// }
|
|
|
|
|
-// }
|
|
|
|
|
|
|
+ // private InputStream getObjectContent(String fileCode, String path) {
|
|
|
|
|
+ // String filePath = VbFile.getDirectory(path, fileCode);
|
|
|
|
|
+ // VbFile vbFile = new VbFile(filePath, fileCode);
|
|
|
|
|
+ // try {
|
|
|
|
|
+ // try (InputStream stream = new FileInputStream(vbFile)) {
|
|
|
|
|
+ // return stream;
|
|
|
|
|
+ // }
|
|
|
|
|
+ // } catch (Exception e) {
|
|
|
|
|
+ // log.error("获取文件流异常: {}", e.getMessage());
|
|
|
|
|
+ // throw new OssException("获取文件流异常: " + e.getMessage());
|
|
|
|
|
+ // }
|
|
|
|
|
+ // }
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 获取云存储服务的URL
|
|
* 获取云存储服务的URL
|
|
@@ -629,8 +637,9 @@ public class OssClient {
|
|
|
// MinIO 单独处理
|
|
// MinIO 单独处理
|
|
|
if (StringUtils.isNotEmpty(domain)) {
|
|
if (StringUtils.isNotEmpty(domain)) {
|
|
|
// 如果 domain 以 "https://" 或 "http://" 开头
|
|
// 如果 domain 以 "https://" 或 "http://" 开头
|
|
|
- return (domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP)) ?
|
|
|
|
|
- domain + StringUtils.SLASH + properties.getBucketName() : header + domain + StringUtils.SLASH + properties.getBucketName();
|
|
|
|
|
|
|
+ return (domain.startsWith(Constants.HTTPS) || domain.startsWith(Constants.HTTP))
|
|
|
|
|
+ ? domain + StringUtils.SLASH + properties.getBucketName()
|
|
|
|
|
+ : header + domain + StringUtils.SLASH + properties.getBucketName();
|
|
|
}
|
|
}
|
|
|
return header + endpoint + StringUtils.SLASH + properties.getBucketName();
|
|
return header + endpoint + StringUtils.SLASH + properties.getBucketName();
|
|
|
}
|
|
}
|
|
@@ -656,8 +665,8 @@ public class OssClient {
|
|
|
// 生成日期路径
|
|
// 生成日期路径
|
|
|
String datePath = DateUtils.datePath();
|
|
String datePath = DateUtils.datePath();
|
|
|
// 拼接路径
|
|
// 拼接路径
|
|
|
- path = StringUtils.isNotEmpty(prefix) ?
|
|
|
|
|
- prefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid : datePath + StringUtils.SLASH + uuid;
|
|
|
|
|
|
|
+ path = StringUtils.isNotEmpty(prefix) ? prefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid
|
|
|
|
|
+ : datePath + StringUtils.SLASH + uuid;
|
|
|
return path + suffix;
|
|
return path + suffix;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -670,17 +679,38 @@ public class OssClient {
|
|
|
* @param objectKey 对象KEY
|
|
* @param objectKey 对象KEY
|
|
|
* @param expiredTime 链接授权到期时间
|
|
* @param expiredTime 链接授权到期时间
|
|
|
*/
|
|
*/
|
|
|
- public String getPrivateUrl(String objectKey, Duration expiredTime) {
|
|
|
|
|
- // 使用 AWS S3 预签名 URL 的生成器 获取对象的预签名 URL
|
|
|
|
|
|
|
+ public String createPresignedGetUrl(String objectKey, Duration expiredTime) {
|
|
|
|
|
+ // 使用 AWS S3 预签名 URL 的生成器 获取下载对象的预签名 URL
|
|
|
URL url = presigner.presignGetObject(
|
|
URL url = presigner.presignGetObject(
|
|
|
- x -> x.signatureDuration(expiredTime)
|
|
|
|
|
- .getObjectRequest(
|
|
|
|
|
- y -> y.bucket(properties.getBucketName())
|
|
|
|
|
- .key(objectKey)
|
|
|
|
|
- .build())
|
|
|
|
|
- .build())
|
|
|
|
|
|
|
+ x -> x.signatureDuration(expiredTime)
|
|
|
|
|
+ .getObjectRequest(
|
|
|
|
|
+ y -> y.bucket(properties.getBucketName())
|
|
|
|
|
+ .key(objectKey)
|
|
|
|
|
+ .build())
|
|
|
|
|
+ .build())
|
|
|
|
|
+ .url();
|
|
|
|
|
+ return url.toExternalForm();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建上传请求的预签名URL
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param objectKey 对象KEY
|
|
|
|
|
+ * @param expiredTime 链接授权到期时间
|
|
|
|
|
+ * @param metadata 元数据
|
|
|
|
|
+ */
|
|
|
|
|
+ public String createPresignedPutUrl(String objectKey, Duration expiredTime, Map<String, String> metadata) {
|
|
|
|
|
+ // 使用 AWS S3 预签名 URL 的生成器 获取上传文件对象的预签名 URL
|
|
|
|
|
+ URL url = presigner.presignPutObject(
|
|
|
|
|
+ x -> x.signatureDuration(expiredTime)
|
|
|
|
|
+ .putObjectRequest(
|
|
|
|
|
+ y -> y.bucket(properties.getBucketName())
|
|
|
|
|
+ .key(objectKey)
|
|
|
|
|
+ .metadata(metadata)
|
|
|
|
|
+ .build())
|
|
|
|
|
+ .build())
|
|
|
.url();
|
|
.url();
|
|
|
- return url.toString();
|
|
|
|
|
|
|
+ return url.toExternalForm();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -699,7 +729,6 @@ public class OssClient {
|
|
|
return AccessPolicyType.getByType(properties.getAccessPolicy());
|
|
return AccessPolicyType.getByType(properties.getAccessPolicy());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
public boolean isLocal() {
|
|
public boolean isLocal() {
|
|
|
return LOCAL_ACCESS_KEY.equals(configKey);
|
|
return LOCAL_ACCESS_KEY.equals(configKey);
|
|
|
}
|
|
}
|