Commit 46d7e9a98c1fa8115f94da691de847a950a25699

Authored by preschooler
1 parent f8ebdbdd

🎈 perf:

优化 S3 对象存储使支持服务端内网传输客户端外网传输、
修复编辑主配置无法立即生效问题
将唯一值作为文件上级路径以保留原文件名
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java
... ... @@ -53,6 +53,7 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
53 53 AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
54 54 AwsBasicCredentials.create(config.getAccessKey(), config.getAccessSecret()));
55 55 URI endpoint = URI.create(buildEndpoint());
  56 + URI presignerEndpoint = URI.create(buildPresignerEndpoint());
56 57 S3Configuration serviceConfiguration = S3Configuration.builder() // Path-style 访问
57 58 .pathStyleAccessEnabled(Boolean.TRUE.equals(config.getEnablePathStyleAccess()))
58 59 .chunkedEncodingEnabled(false) // 禁用分块编码,参见 https://t.zsxq.com/kBy57
... ... @@ -66,7 +67,7 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
66 67 presigner = S3Presigner.builder()
67 68 .credentialsProvider(credentialsProvider)
68 69 .region(region)
69   - .endpointOverride(endpoint)
  70 + .endpointOverride(presignerEndpoint)
70 71 .serviceConfiguration(serviceConfiguration)
71 72 .build();
72 73 }
... ... @@ -162,6 +163,23 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
162 163 }
163 164  
164 165 /**
  166 + * presigner 节点地址
  167 + *
  168 + * @return 节点地址
  169 + */
  170 + private String buildPresignerEndpoint() {
  171 + // 补全 domain
  172 + if (StrUtil.isEmpty(config.getDomain())) {
  173 + config.setDomain(buildDomain());
  174 + }
  175 +
  176 + if (Boolean.TRUE.equals(config.getEnablePathStyleAccess())) {
  177 + return StrUtil.removeSuffix(config.getDomain(), StrUtil.format("/{}", config.getBucket()));
  178 + }
  179 + return StrUtil.replace(config.getDomain(), StrUtil.format("://{}.", config.getBucket()), "://");
  180 + }
  181 +
  182 + /**
165 183 * 解析 AWS 区域
166 184 * 优先级:配置的 region > 从 endpoint 解析的 region > 默认值 us-east-1
167 185 *
... ...
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.infra.service.file;
2 2  
3 3 import cn.hutool.core.io.resource.ResourceUtil;
4 4 import cn.hutool.core.util.IdUtil;
  5 +import cn.hutool.core.util.StrUtil;
5 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
7 8 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
... ... @@ -93,7 +94,7 @@ public class FileConfigServiceImpl implements FileConfigService {
93 94 fileConfigMapper.updateById(updateObj);
94 95  
95 96 // 清空缓存
96   - clearCache(config.getId(), null);
  97 + clearCache(config.getId(), config.getMaster());
97 98 }
98 99  
99 100 @Override
... ... @@ -132,7 +133,7 @@ public class FileConfigServiceImpl implements FileConfigService {
132 133 fileConfigMapper.deleteById(id);
133 134  
134 135 // 清空缓存
135   - clearCache(id, null);
  136 + clearCache(id, config.getMaster());
136 137 }
137 138  
138 139 @Override
... ... @@ -149,7 +150,7 @@ public class FileConfigServiceImpl implements FileConfigService {
149 150 fileConfigMapper.deleteByIds(ids);
150 151  
151 152 // 清空缓存
152   - ids.forEach(id -> clearCache(id, null));
  153 + ids.forEach(id -> clearCache(id, false));
153 154 }
154 155  
155 156 /**
... ... @@ -191,7 +192,7 @@ public class FileConfigServiceImpl implements FileConfigService {
191 192 validateFileConfigExists(id);
192 193 // 上传文件
193 194 byte[] content = ResourceUtil.readBytes("file/erweima.jpg");
194   - return getFileClient(id).upload(content, IdUtil.fastSimpleUUID() + ".jpg", "image/jpeg");
  195 + return getFileClient(id).upload(content, "public" + StrUtil.SLASH + IdUtil.fastSimpleUUID() + ".jpg", "image/jpeg");
195 196 }
196 197  
197 198 @Override
... ...
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java
... ... @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.infra.service.file;
3 3 import cn.hutool.core.date.LocalDateTimeUtil;
4 4 import cn.hutool.core.io.FileUtil;
5 5 import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.util.RandomUtil;
6 7 import cn.hutool.core.util.StrUtil;
7 8 import cn.hutool.crypto.digest.DigestUtil;
8 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
... ... @@ -101,17 +102,12 @@ public class FileServiceImpl implements FileService {
101 102 }
102 103 String suffix = null;
103 104 if (PATH_SUFFIX_TIMESTAMP_ENABLE) {
104   - suffix = String.valueOf(System.currentTimeMillis());
  105 + suffix = String.valueOf(System.currentTimeMillis()) + RandomUtil.randomInt(10000, 100000);
105 106 }
106 107  
107 108 // 2.1 先拼接 suffix 后缀
108 109 if (StrUtil.isNotEmpty(suffix)) {
109   - String ext = FileUtil.extName(name);
110   - if (StrUtil.isNotEmpty(ext)) {
111   - name = FileUtil.mainName(name) + StrUtil.C_UNDERLINE + suffix + StrUtil.DOT + ext;
112   - } else {
113   - name = name + StrUtil.C_UNDERLINE + suffix;
114   - }
  110 + name = suffix + StrUtil.SLASH + name;
115 111 }
116 112 // 2.2 再拼接 prefix 前缀
117 113 if (StrUtil.isNotEmpty(prefix)) {
... ...
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java
... ... @@ -93,7 +93,7 @@ public class FileServiceImplTest extends BaseDbUnitTest {
93 93 String url = randomString();
94 94 AtomicReference<String> pathRef = new AtomicReference<>();
95 95 when(client.upload(same(content), argThat(path -> {
96   - assertTrue(path.matches(directory + "/\\d{8}/" + name + "_\\d+.jpg"));
  96 + assertTrue(path.matches(directory + "/\\d{8}/\\d+/" + name + ".jpg"));
97 97 pathRef.set(path);
98 98 return true;
99 99 }), eq(type))).thenReturn(url);
... ... @@ -125,7 +125,7 @@ public class FileServiceImplTest extends BaseDbUnitTest {
125 125 String url = randomString();
126 126 AtomicReference<String> pathRef = new AtomicReference<>();
127 127 when(client.upload(same(content), argThat(path -> {
128   - assertTrue(path.matches("\\d{8}/6318848e882d8a7e7e82789d87608f684ee52d41966bfc8cad3ce15aad2b970e_\\d+\.jpg"));
  128 + assertTrue(path.matches("\\d{8}/\\d+/6318848e882d8a7e7e82789d87608f684ee52d41966bfc8cad3ce15aad2b970e\.jpg"));
129 129 pathRef.set(path);
130 130 return true;
131 131 }), eq(type))).thenReturn(url);
... ... @@ -200,10 +200,10 @@ public class FileServiceImplTest extends BaseDbUnitTest {
200 200 String path = fileService.generateUploadPath(name, directory);
201 201  
202 202 // 断言
203   - // 格式为:avatar/yyyyMMdd/test_timestamp.jpg
  203 + // 格式为:avatar/yyyyMMdd/timestamp/test.jpg
204 204 assertTrue(path.startsWith(directory + "/"));
205 205 // 包含日期格式:8 位数字,如 20240517
206   - assertTrue(path.matches(directory + "/\\d{8}/test_\\d+\.jpg"));
  206 + assertTrue(path.matches(directory + "/\\d{8}/\\d+/test\.jpg"));
207 207 }
208 208  
209 209 @Test
... ... @@ -236,9 +236,9 @@ public class FileServiceImplTest extends BaseDbUnitTest {
236 236 String path = fileService.generateUploadPath(name, directory);
237 237  
238 238 // 断言
239   - // 格式为:avatar/test_timestamp.jpg
  239 + // 格式为:avatar/timestamp/test.jpg
240 240 assertTrue(path.startsWith(directory + "/"));
241   - assertTrue(path.matches(directory + "/test_\\d+\.jpg"));
  241 + assertTrue(path.matches(directory + "/\\d+/test\.jpg"));
242 242 }
243 243  
244 244 @Test
... ... @@ -269,9 +269,9 @@ public class FileServiceImplTest extends BaseDbUnitTest {
269 269 String path = fileService.generateUploadPath(name, directory);
270 270  
271 271 // 断言
272   - // 格式为:avatar/yyyyMMdd/test_timestamp
  272 + // 格式为:avatar/yyyyMMdd/timestamp/test
273 273 assertTrue(path.startsWith(directory + "/"));
274   - assertTrue(path.matches(directory + "/\\d{8}/test_\\d+"));
  274 + assertTrue(path.matches(directory + "/\\d{8}/\\d+/test"));
275 275 }
276 276  
277 277 @Test
... ... @@ -286,8 +286,8 @@ public class FileServiceImplTest extends BaseDbUnitTest {
286 286 String path = fileService.generateUploadPath(name, directory);
287 287  
288 288 // 断言
289   - // 格式为:yyyyMMdd/test_timestamp.jpg
290   - assertTrue(path.matches("\\d{8}/test_\\d+\\.jpg"));
  289 + // 格式为:yyyyMMdd/timestamp/test.jpg
  290 + assertTrue(path.matches("\\d{8}/\\d+/test\\.jpg"));
291 291 }
292 292  
293 293 @Test
... ... @@ -302,8 +302,8 @@ public class FileServiceImplTest extends BaseDbUnitTest {
302 302 String path = fileService.generateUploadPath(name, directory);
303 303  
304 304 // 断言
305   - // 格式为:yyyyMMdd/test_timestamp.jpg
306   - assertTrue(path.matches("\\d{8}/test_\\d+\\.jpg"));
  305 + // 格式为:yyyyMMdd/timestamp/test.jpg
  306 + assertTrue(path.matches("\\d{8}/\\d+/test\\.jpg"));
307 307 }
308 308  
309 309 }
... ...