feat(framework): OssPresignResponseBodyAdvice 支持 List<String> 字段预签名

- @OssPresignUrl 注解现在同时适用于 String 和 List<String> 字段
- 回填时防御不可变 List,自动降级为 ArrayList
- FileServiceImpl 预签名访问 URL 去除查询参数,保持持久化路径干净

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lzh
2026-03-22 14:59:34 +08:00
parent b02059a08a
commit e11d3e1b6e
2 changed files with 38 additions and 5 deletions

View File

@@ -130,8 +130,21 @@ public class OssPresignResponseBodyAdvice implements ResponseBodyAdvice<Object>
String signed = signedMap.get(entry.url);
if (signed != null) {
try {
entry.field.set(entry.target, signed);
} catch (IllegalAccessException e) {
if (entry.isList()) {
@SuppressWarnings("unchecked")
List<String> list = (List<String>) entry.field.get(entry.target);
try {
list.set(entry.index(), signed);
} catch (UnsupportedOperationException ex) {
// 不可变 List如 List.of()),替换为可变 ArrayList 后重试
List<String> mutable = new ArrayList<>(list);
mutable.set(entry.index(), signed);
entry.field.set(entry.target, mutable);
}
} else {
entry.field.set(entry.target, signed);
}
} catch (Exception e) {
log.warn("[OssPresignResponseBodyAdvice] 回填签名 URL 失败: field={}", entry.field.getName(), e);
}
}
@@ -195,6 +208,15 @@ public class OssPresignResponseBodyAdvice implements ResponseBodyAdvice<Object>
if (value instanceof String url && StrUtil.isNotEmpty(url)) {
entries.add(new FieldEntry(obj, field, url));
}
// 带注解的 List<String> 字段:逐个收集 URL
if (value instanceof List<?> list) {
for (int i = 0; i < list.size(); i++) {
Object item = list.get(i);
if (item instanceof String url && StrUtil.isNotEmpty(url)) {
entries.add(new FieldEntry(obj, field, i, url));
}
}
}
} else {
// 非简单类型字段:递归(递归入口会再次判断 isProjectClass
collectUrls(value, entries, visited, depth + 1);
@@ -221,7 +243,8 @@ public class OssPresignResponseBodyAdvice implements ResponseBodyAdvice<Object>
return FIELD_CACHE.computeIfAbsent(clazz, c -> {
Set<Field> result = new HashSet<>();
for (Field field : getAllFields(c)) {
if (field.isAnnotationPresent(OssPresignUrl.class) && field.getType() == String.class) {
if (field.isAnnotationPresent(OssPresignUrl.class)
&& (field.getType() == String.class || List.class.isAssignableFrom(field.getType()))) {
result.add(field);
}
}
@@ -340,6 +363,16 @@ public class OssPresignResponseBodyAdvice implements ResponseBodyAdvice<Object>
}
}
private record FieldEntry(Object target, Field field, String url) {}
private record FieldEntry(Object target, Field field, int index, String url) {
/** String 字段构造 */
FieldEntry(Object target, Field field, String url) {
this(target, field, -1, url);
}
boolean isList() {
return index >= 0;
}
}
}

View File

@@ -134,7 +134,7 @@ public class FileServiceImpl implements FileService {
// 2. 获取文件预签名地址
FileClient fileClient = fileConfigService.getMasterFileClient();
String uploadUrl = fileClient.presignPutUrl(path);
String visitUrl = fileClient.presignGetUrl(path, null);
String visitUrl = HttpUtils.removeUrlQuery(fileClient.presignGetUrl(path, null));
return new FilePresignedUrlRespVO().setConfigId(fileClient.getId())
.setPath(path).setUploadUrl(uploadUrl).setUrl(visitUrl);
}