mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-03-28 16:23:24 +08:00
update 优化 oss 模块代码实现
This commit is contained in:
@@ -2,6 +2,7 @@ package org.dromara.common.oss.client;
|
|||||||
|
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import org.dromara.common.core.utils.DateUtils;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.oss.config.OssClientConfig;
|
import org.dromara.common.oss.config.OssClientConfig;
|
||||||
import org.dromara.common.oss.exception.S3StorageException;
|
import org.dromara.common.oss.exception.S3StorageException;
|
||||||
@@ -127,27 +128,6 @@ public abstract class AbstractOssClientImpl implements OssClient {
|
|||||||
|
|
||||||
abstract void doInitialize();
|
abstract void doInitialize();
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh(OssClientConfig config) {
|
|
||||||
if (Objects.equals(this.config, config)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 如果状态本来就是未初始化,直接则调用初始化
|
|
||||||
if (!initialized.get()) {
|
|
||||||
this.initialize();
|
|
||||||
}
|
|
||||||
// 将状态转为未初始化
|
|
||||||
if (initialized.compareAndSet(false, true)) {
|
|
||||||
try {
|
|
||||||
this.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 异常不影响刷新逻辑,此处屏蔽异常
|
|
||||||
}
|
|
||||||
// 状态交换成功才进行刷新
|
|
||||||
this.initialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean verifyConfig(Function<OssClientConfig, Boolean> verifyConfigAction) {
|
public boolean verifyConfig(Function<OssClientConfig, Boolean> verifyConfigAction) {
|
||||||
OssClientConfig config = config();
|
OssClientConfig config = config();
|
||||||
@@ -159,6 +139,23 @@ public abstract class AbstractOssClientImpl implements OssClient {
|
|||||||
return verifyConfig((config) -> Objects.equals(config, verifyConfig));
|
return verifyConfig((config) -> Objects.equals(config, verifyConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String buildPathKey(String fileName) {
|
||||||
|
return buildPathKey(null, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String buildPathKey(String businessPrefix, String fileName) {
|
||||||
|
String defaultPrefix = config.prefix()
|
||||||
|
.orElse("");
|
||||||
|
String mergedPrefix = mergePrefix(defaultPrefix, businessPrefix);
|
||||||
|
String suffix = suffix(fileName);
|
||||||
|
String datePath = DateUtils.datePath();
|
||||||
|
String uuid = IdUtil.fastSimpleUUID();
|
||||||
|
String path = mergedPrefix.isEmpty() ? datePath + StringUtils.SLASH + uuid : mergedPrefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid;
|
||||||
|
return path + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T doCustomUpload(AsyncRequestBody body, Consumer<PutObjectRequest.Builder> putObjectRequestBuilderConsumer, Collection<TransferListener> transferListeners, BiFunction<CompletedUpload, Throwable, T> handleAsyncAction) {
|
public <T> T doCustomUpload(AsyncRequestBody body, Consumer<PutObjectRequest.Builder> putObjectRequestBuilderConsumer, Collection<TransferListener> transferListeners, BiFunction<CompletedUpload, Throwable, T> handleAsyncAction) {
|
||||||
try {
|
try {
|
||||||
@@ -230,7 +227,7 @@ public abstract class AbstractOssClientImpl implements OssClient {
|
|||||||
try {
|
try {
|
||||||
// 以文件的大小为准
|
// 以文件的大小为准
|
||||||
options.setLength(file.length());
|
options.setLength(file.length());
|
||||||
return bucketUpload(bucket, key, file.getChannel(), -1L);
|
return bucketUpload(bucket, key, file.getChannel(), -1L, options);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (e instanceof S3StorageException ex) {
|
if (e instanceof S3StorageException ex) {
|
||||||
throw ex;
|
throw ex;
|
||||||
@@ -438,8 +435,8 @@ public abstract class AbstractOssClientImpl implements OssClient {
|
|||||||
@Override
|
@Override
|
||||||
public boolean bucketDelete(String bucket, String key) {
|
public boolean bucketDelete(String bucket, String key) {
|
||||||
try {
|
try {
|
||||||
DeleteObjectResponse response = s3AsyncClient.deleteObject(builder -> builder.bucket(bucket).key(key)).join();
|
s3AsyncClient.deleteObject(builder -> builder.bucket(bucket).key(key)).join();
|
||||||
return Boolean.TRUE.equals(response.deleteMarker());
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw S3StorageException.form(e);
|
throw S3StorageException.form(e);
|
||||||
}
|
}
|
||||||
@@ -589,6 +586,43 @@ public abstract class AbstractOssClientImpl implements OssClient {
|
|||||||
.orElseThrow(() -> S3StorageException.form("bucket is not configured."));
|
.orElseThrow(() -> S3StorageException.form("bucket is not configured."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String mergePrefix(String defaultPrefix, String businessPrefix) {
|
||||||
|
String left = normalizePrefix(defaultPrefix);
|
||||||
|
String right = normalizePrefix(businessPrefix);
|
||||||
|
if (left.isEmpty()) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
if (right.isEmpty()) {
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
return left + StringUtils.SLASH + right;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizePrefix(String prefix) {
|
||||||
|
if (prefix == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String normalized = prefix.trim();
|
||||||
|
while (normalized.startsWith(StringUtils.SLASH)) {
|
||||||
|
normalized = normalized.substring(1);
|
||||||
|
}
|
||||||
|
while (normalized.endsWith(StringUtils.SLASH)) {
|
||||||
|
normalized = normalized.substring(0, normalized.length() - 1);
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String suffix(String fileName) {
|
||||||
|
if (fileName == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
int index = fileName.lastIndexOf('.');
|
||||||
|
if (index < 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return fileName.substring(index);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
if (s3TransferManager != null) {
|
if (s3TransferManager != null) {
|
||||||
|
|||||||
@@ -68,13 +68,6 @@ public interface OssClient extends AutoCloseable {
|
|||||||
*/
|
*/
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新客户端配置
|
|
||||||
*
|
|
||||||
* @param config 配置项
|
|
||||||
*/
|
|
||||||
void refresh(OssClientConfig config);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验客户端配置
|
* 校验客户端配置
|
||||||
*
|
*
|
||||||
@@ -585,4 +578,21 @@ public interface OssClient extends AutoCloseable {
|
|||||||
* @return 预签名上传 URL
|
* @return 预签名上传 URL
|
||||||
*/
|
*/
|
||||||
String presignPutUrl(String key, Duration expiredTime, Map<String, String> metadata);
|
String presignPutUrl(String key, Duration expiredTime, Map<String, String> metadata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据客户端配置生成默认对象Key。
|
||||||
|
*
|
||||||
|
* @param fileName 原始文件名
|
||||||
|
* @return 对象Key
|
||||||
|
*/
|
||||||
|
String buildPathKey(String fileName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据业务前缀和客户端默认前缀生成对象Key。
|
||||||
|
*
|
||||||
|
* @param businessPrefix 业务前缀
|
||||||
|
* @param fileName 原始文件名
|
||||||
|
* @return 对象Key
|
||||||
|
*/
|
||||||
|
String buildPathKey(String businessPrefix, String fileName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ public class OssClientConfig implements Config<OssClientConfig, OssClientConfig.
|
|||||||
.secretKey(properties.getSecretKey())
|
.secretKey(properties.getSecretKey())
|
||||||
.bucket(properties.getBucketName())
|
.bucket(properties.getBucketName())
|
||||||
.region(region)
|
.region(region)
|
||||||
|
.prefix(properties.getPrefix())
|
||||||
.useHttps(SystemConstants.YES.equals(properties.getIsHttps()))
|
.useHttps(SystemConstants.YES.equals(properties.getIsHttps()))
|
||||||
.usePathStyleAccess(usePathStyleAccess)
|
.usePathStyleAccess(usePathStyleAccess)
|
||||||
.accessControlPolicyConfig(accessControlPolicyConfig);
|
.accessControlPolicyConfig(accessControlPolicyConfig);
|
||||||
|
|||||||
@@ -52,19 +52,16 @@ public class OssFactory {
|
|||||||
OssClientConfig config = OssClientConfig.formProperties(properties);
|
OssClientConfig config = OssClientConfig.formProperties(properties);
|
||||||
LOCK.lock();
|
LOCK.lock();
|
||||||
try {
|
try {
|
||||||
// 如果已经存在,则校验配置一致性
|
OssClient client = CLIENT_CACHE.get(configKey);
|
||||||
if (CLIENT_CACHE.containsKey(configKey)) {
|
if (client != null) {
|
||||||
OssClient client = CLIENT_CACHE.get(configKey);
|
if (client.verifyConfig(config)) {
|
||||||
if (!client.verifyConfig(config)) {
|
return client;
|
||||||
// 配置不一致,刷新配置
|
|
||||||
client.refresh(config);
|
|
||||||
CLIENT_CACHE.put(configKey, client);
|
|
||||||
}
|
}
|
||||||
return client;
|
closeClient(configKey, client);
|
||||||
}
|
}
|
||||||
DefaultOssClientImpl client = new DefaultOssClientImpl(configKey, config);
|
OssClient newClient = new DefaultOssClientImpl(configKey, config);
|
||||||
CLIENT_CACHE.put(configKey, client);
|
CLIENT_CACHE.put(configKey, newClient);
|
||||||
return client;
|
return newClient;
|
||||||
} finally {
|
} finally {
|
||||||
LOCK.unlock();
|
LOCK.unlock();
|
||||||
}
|
}
|
||||||
@@ -78,12 +75,16 @@ public class OssFactory {
|
|||||||
if (client == null) {
|
if (client == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
closeClient(configKey, client);
|
||||||
client.close();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("S3存储客户端关闭异常,错误信息: {}", e.getMessage(), e);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void closeClient(String configKey, OssClient client) {
|
||||||
|
try {
|
||||||
|
client.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("S3存储客户端 [{}] 关闭异常,错误信息: {}", configKey, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
package org.dromara.common.oss.util;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.IdUtil;
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import org.dromara.common.core.utils.DateUtils;
|
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* S3文件对象工具类
|
|
||||||
*
|
|
||||||
* @author 秋辞未寒
|
|
||||||
*/
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public class S3ObjectUtil {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成一个 【自定义前缀 + 日期路径 + SimpleUUID.文件后缀】 的对象Key 示例: images/20260321/019d0f89c9b1130a48c90dbca0475a.jpg
|
|
||||||
*
|
|
||||||
* @param prefix 前缀
|
|
||||||
* @param withSuffixFileName 带后缀的文件名
|
|
||||||
* @return 文件路径对象Key
|
|
||||||
*/
|
|
||||||
public static String buildPathKey(String prefix, String withSuffixFileName) {
|
|
||||||
// 获取后缀
|
|
||||||
String suffix = StringUtils.substring(withSuffixFileName, withSuffixFileName.lastIndexOf("."), withSuffixFileName.length());
|
|
||||||
// 生成日期路径
|
|
||||||
String datePath = DateUtils.datePath();
|
|
||||||
// 生成uuid
|
|
||||||
String uuid = IdUtil.fastSimpleUUID();
|
|
||||||
// 拼接路径
|
|
||||||
String path = StringUtils.isNotEmpty(prefix) ? prefix + StringUtils.SLASH + datePath + StringUtils.SLASH + uuid : datePath + StringUtils.SLASH + uuid;
|
|
||||||
return path + suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成一个 【日期路径 + SimpleUUID.文件后缀】 的对象Key 示例: 20260321/019d0f89c9b1130a48c90dbca0475a.jpg
|
|
||||||
*
|
|
||||||
* @param withSuffixFileName 带后缀的文件名
|
|
||||||
* @return 文件路径对象Key
|
|
||||||
*/
|
|
||||||
public static String buildPathKey(String withSuffixFileName) {
|
|
||||||
return buildPathKey("", withSuffixFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -26,7 +26,6 @@ import org.dromara.common.oss.enums.AccessPolicy;
|
|||||||
import org.dromara.common.oss.factory.OssFactory;
|
import org.dromara.common.oss.factory.OssFactory;
|
||||||
import org.dromara.common.oss.model.Options;
|
import org.dromara.common.oss.model.Options;
|
||||||
import org.dromara.common.oss.model.PutObjectResult;
|
import org.dromara.common.oss.model.PutObjectResult;
|
||||||
import org.dromara.common.oss.util.S3ObjectUtil;
|
|
||||||
import org.dromara.system.domain.SysOss;
|
import org.dromara.system.domain.SysOss;
|
||||||
import org.dromara.system.domain.SysOssExt;
|
import org.dromara.system.domain.SysOssExt;
|
||||||
import org.dromara.system.domain.bo.SysOssBo;
|
import org.dromara.system.domain.bo.SysOssBo;
|
||||||
@@ -232,11 +231,9 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
|
|||||||
String originalfileName = file.getOriginalFilename();
|
String originalfileName = file.getOriginalFilename();
|
||||||
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
||||||
OssClient instance = OssFactory.instance();
|
OssClient instance = OssFactory.instance();
|
||||||
try {
|
String pathKey = instance.buildPathKey(originalfileName);
|
||||||
String pathKey = S3ObjectUtil.buildPathKey(originalfileName);
|
try (InputStream inputStream = file.getInputStream()) {
|
||||||
InputStream inputStream = file.getInputStream();
|
|
||||||
PutObjectResult result = instance.upload(pathKey, inputStream, file.getSize(), Options.builder().setContentType(file.getContentType()));
|
PutObjectResult result = instance.upload(pathKey, inputStream, file.getSize(), Options.builder().setContentType(file.getContentType()));
|
||||||
IoUtil.close(inputStream);
|
|
||||||
SysOssExt ext1 = new SysOssExt();
|
SysOssExt ext1 = new SysOssExt();
|
||||||
ext1.setFileSize(file.getSize());
|
ext1.setFileSize(file.getSize());
|
||||||
ext1.setContentType(file.getContentType());
|
ext1.setContentType(file.getContentType());
|
||||||
@@ -261,7 +258,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
|
|||||||
String originalfileName = file.getName();
|
String originalfileName = file.getName();
|
||||||
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
||||||
OssClient instance = OssFactory.instance();
|
OssClient instance = OssFactory.instance();
|
||||||
String pathKey = S3ObjectUtil.buildPathKey(originalfileName);
|
String pathKey = instance.buildPathKey(originalfileName);
|
||||||
PutObjectResult result = instance.upload(pathKey, file, Options.builder().setContentType(FileUtils.getMimeType(file.toPath())));
|
PutObjectResult result = instance.upload(pathKey, file, Options.builder().setContentType(FileUtils.getMimeType(file.toPath())));
|
||||||
SysOssExt ext1 = new SysOssExt();
|
SysOssExt ext1 = new SysOssExt();
|
||||||
ext1.setFileSize(result.size());
|
ext1.setFileSize(result.size());
|
||||||
|
|||||||
Reference in New Issue
Block a user