Skip to content

Commit 2c178b4

Browse files
authored
optimize(framework): 修复文件上传防重复摘要栈溢出 (#62)
1 parent dbedd45 commit 2c178b4

2 files changed

Lines changed: 73 additions & 1 deletion

File tree

framework/src/main/java/com/nageoffer/ai/ragent/framework/idempotent/IdempotentSubmitAspect.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
import cn.hutool.core.util.StrUtil;
2121
import cn.hutool.crypto.digest.DigestUtil;
2222
import com.google.gson.Gson;
23+
import com.google.gson.GsonBuilder;
24+
import com.google.gson.JsonObject;
25+
import com.google.gson.JsonSerializer;
2326
import com.nageoffer.ai.ragent.framework.context.UserContext;
2427
import com.nageoffer.ai.ragent.framework.exception.ClientException;
2528
import lombok.RequiredArgsConstructor;
@@ -33,6 +36,7 @@
3336
import org.springframework.stereotype.Component;
3437
import org.springframework.web.context.request.RequestContextHolder;
3538
import org.springframework.web.context.request.ServletRequestAttributes;
39+
import org.springframework.web.multipart.MultipartFile;
3640

3741
import java.lang.reflect.Method;
3842
import java.nio.charset.StandardCharsets;
@@ -47,7 +51,18 @@
4751
public final class IdempotentSubmitAspect {
4852

4953
private final RedissonClient redissonClient;
50-
private final Gson gson = new Gson();
54+
private final Gson gson = new GsonBuilder()
55+
.registerTypeHierarchyAdapter(
56+
MultipartFile.class,
57+
(JsonSerializer<MultipartFile>) (file, type, context) -> {
58+
JsonObject json = new JsonObject();
59+
json.addProperty("name", file.getName());
60+
json.addProperty("originalFilename", file.getOriginalFilename());
61+
json.addProperty("contentType", file.getContentType());
62+
json.addProperty("size", file.getSize());
63+
return json;
64+
})
65+
.create();
5166

5267
@Value("${app.eval.enabled:false}")
5368
private boolean evalEnabled;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package com.nageoffer.ai.ragent.framework.idempotent;
19+
20+
import org.aspectj.lang.ProceedingJoinPoint;
21+
import org.junit.jupiter.api.Test;
22+
import org.redisson.api.RedissonClient;
23+
import org.springframework.mock.web.MockMultipartFile;
24+
25+
import java.lang.reflect.Method;
26+
27+
import static org.junit.jupiter.api.Assertions.assertEquals;
28+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
29+
import static org.mockito.Mockito.mock;
30+
import static org.mockito.Mockito.when;
31+
32+
class IdempotentSubmitAspectTest {
33+
34+
@Test
35+
void shouldGenerateDigestFromMultipartMetadataWithoutReadingContent() throws Exception {
36+
byte[] firstContent = new byte[20 * 1024 * 1024];
37+
byte[] differentContent = firstContent.clone();
38+
differentContent[differentContent.length - 1] = 1;
39+
MockMultipartFile first = new MockMultipartFile("file", "first.pdf", "application/pdf", firstContent);
40+
MockMultipartFile sameMetadata =
41+
new MockMultipartFile("file", "first.pdf", "application/pdf", differentContent);
42+
MockMultipartFile differentName =
43+
new MockMultipartFile("file", "second.pdf", "application/pdf", firstContent);
44+
45+
assertEquals(calcArgsMd5(first), calcArgsMd5(sameMetadata));
46+
assertNotEquals(calcArgsMd5(first), calcArgsMd5(differentName));
47+
}
48+
49+
private String calcArgsMd5(MockMultipartFile file) throws Exception {
50+
ProceedingJoinPoint joinPoint = mock(ProceedingJoinPoint.class);
51+
when(joinPoint.getArgs()).thenReturn(new Object[]{file});
52+
IdempotentSubmitAspect aspect = new IdempotentSubmitAspect(mock(RedissonClient.class));
53+
Method method = IdempotentSubmitAspect.class.getDeclaredMethod("calcArgsMD5", ProceedingJoinPoint.class);
54+
method.setAccessible(true);
55+
return (String) method.invoke(aspect, joinPoint);
56+
}
57+
}

0 commit comments

Comments
 (0)