发布 v1.10 #5
@@ -47,7 +47,7 @@ import java.util.Set;
|
|||||||
@UsePermission(moduleName = "/api/v1/documentCollection")
|
@UsePermission(moduleName = "/api/v1/documentCollection")
|
||||||
public class FaqItemController extends BaseCurdController<FaqItemService, FaqItem> {
|
public class FaqItemController extends BaseCurdController<FaqItemService, FaqItem> {
|
||||||
|
|
||||||
private static final long MAX_IMAGE_SIZE_BYTES = 5L * 1024L * 1024L;
|
private static final long MAX_IMAGE_SIZE_BYTES = 20L * 1024L * 1024L;
|
||||||
private static final Set<String> ALLOWED_IMAGE_TYPES = new HashSet<>(Arrays.asList(
|
private static final Set<String> ALLOWED_IMAGE_TYPES = new HashSet<>(Arrays.asList(
|
||||||
"image/jpeg",
|
"image/jpeg",
|
||||||
"image/png",
|
"image/png",
|
||||||
@@ -215,7 +215,7 @@ public class FaqItemController extends BaseCurdController<FaqItemService, FaqIte
|
|||||||
throw new BusinessException("图片不能为空");
|
throw new BusinessException("图片不能为空");
|
||||||
}
|
}
|
||||||
if (file.getSize() > MAX_IMAGE_SIZE_BYTES) {
|
if (file.getSize() > MAX_IMAGE_SIZE_BYTES) {
|
||||||
throw new BusinessException("图片大小不能超过5MB");
|
throw new BusinessException("图片大小不能超过20MB");
|
||||||
}
|
}
|
||||||
if (!isAllowedImageType(file)) {
|
if (!isAllowedImageType(file)) {
|
||||||
throw new BusinessException("仅支持 JPG/PNG/WEBP/GIF 图片");
|
throw new BusinessException("仅支持 JPG/PNG/WEBP/GIF 图片");
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ import java.util.Set;
|
|||||||
@RequestMapping("/api/v1/share/knowledge")
|
@RequestMapping("/api/v1/share/knowledge")
|
||||||
public class ShareKnowledgeController {
|
public class ShareKnowledgeController {
|
||||||
|
|
||||||
private static final long MAX_IMAGE_SIZE_BYTES = 5L * 1024L * 1024L;
|
private static final long MAX_IMAGE_SIZE_BYTES = 20L * 1024L * 1024L;
|
||||||
private static final Set<String> ALLOWED_IMAGE_TYPES = new HashSet<>(Arrays.asList(
|
private static final Set<String> ALLOWED_IMAGE_TYPES = new HashSet<>(Arrays.asList(
|
||||||
"image/jpeg",
|
"image/jpeg",
|
||||||
"image/png",
|
"image/png",
|
||||||
@@ -785,7 +785,7 @@ public class ShareKnowledgeController {
|
|||||||
throw new BusinessException("图片不能为空");
|
throw new BusinessException("图片不能为空");
|
||||||
}
|
}
|
||||||
if (file.getSize() > MAX_IMAGE_SIZE_BYTES) {
|
if (file.getSize() > MAX_IMAGE_SIZE_BYTES) {
|
||||||
throw new BusinessException("图片大小不能超过5MB");
|
throw new BusinessException("图片大小不能超过20MB");
|
||||||
}
|
}
|
||||||
if (!isAllowedImageType(file)) {
|
if (!isAllowedImageType(file)) {
|
||||||
throw new BusinessException("仅支持 JPG/PNG/WEBP/GIF 图片");
|
throw new BusinessException("仅支持 JPG/PNG/WEBP/GIF 图片");
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class WorkflowRunningParameterResolver {
|
|||||||
private static final String DEFAULT_START_FORM_DESCRIPTION = "请先补充必要信息,再开始执行工作流。";
|
private static final String DEFAULT_START_FORM_DESCRIPTION = "请先补充必要信息,再开始执行工作流。";
|
||||||
private static final String DEFAULT_START_FORM_SUBMIT_TEXT = "开始";
|
private static final String DEFAULT_START_FORM_SUBMIT_TEXT = "开始";
|
||||||
private static final int FILE_MAX_COUNT = 10;
|
private static final int FILE_MAX_COUNT = 10;
|
||||||
private static final long FILE_MAX_SINGLE_SIZE = 5L * 1024 * 1024;
|
private static final long FILE_MAX_SINGLE_SIZE = 20L * 1024 * 1024;
|
||||||
private static final long FILE_MAX_TOTAL_SIZE = 50L * 1024 * 1024;
|
private static final long FILE_MAX_TOTAL_SIZE = 50L * 1024 * 1024;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
@@ -363,7 +363,7 @@ public class WorkflowRunningParameterResolver {
|
|||||||
}
|
}
|
||||||
Long size = parseLong(fileMap.get("size"));
|
Long size = parseLong(fileMap.get("size"));
|
||||||
if (size != null && size > FILE_MAX_SINGLE_SIZE) {
|
if (size != null && size > FILE_MAX_SINGLE_SIZE) {
|
||||||
throw new BusinessException("文件参数 " + parameterName + " 中单个文件不能超过 5MB");
|
throw new BusinessException("文件参数 " + parameterName + " 中单个文件不能超过 20MB");
|
||||||
}
|
}
|
||||||
if (size != null && size > 0) {
|
if (size != null && size > 0) {
|
||||||
totalSize += size;
|
totalSize += size;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import java.util.Set;
|
|||||||
@Component
|
@Component
|
||||||
public class DocNodeFileContentExtractor {
|
public class DocNodeFileContentExtractor {
|
||||||
private static final int FILE_MAX_COUNT = 10;
|
private static final int FILE_MAX_COUNT = 10;
|
||||||
private static final long FILE_MAX_SINGLE_SIZE = 5L * 1024 * 1024;
|
private static final long FILE_MAX_SINGLE_SIZE = 20L * 1024 * 1024;
|
||||||
private static final long FILE_MAX_TOTAL_SIZE = 50L * 1024 * 1024;
|
private static final long FILE_MAX_TOTAL_SIZE = 50L * 1024 * 1024;
|
||||||
|
|
||||||
private final DocumentParseBridgeService documentParseBridgeService;
|
private final DocumentParseBridgeService documentParseBridgeService;
|
||||||
@@ -145,7 +145,7 @@ public class DocNodeFileContentExtractor {
|
|||||||
}
|
}
|
||||||
Long size = sourceRef.getSize();
|
Long size = sourceRef.getSize();
|
||||||
if (size != null && size > FILE_MAX_SINGLE_SIZE) {
|
if (size != null && size > FILE_MAX_SINGLE_SIZE) {
|
||||||
throw new BusinessException("单个文件不能超过 5MB: " + sourceRef.getFileName());
|
throw new BusinessException("单个文件不能超过 20MB: " + sourceRef.getFileName());
|
||||||
}
|
}
|
||||||
if (size != null && size > 0) {
|
if (size != null && size > 0) {
|
||||||
totalSize += size;
|
totalSize += size;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.junit.Test;
|
|||||||
import tech.easyflow.ai.entity.Workflow;
|
import tech.easyflow.ai.entity.Workflow;
|
||||||
import tech.easyflow.ai.node.SearchDatasetNodeParser;
|
import tech.easyflow.ai.node.SearchDatasetNodeParser;
|
||||||
import tech.easyflow.ai.node.WorkflowNodeParser;
|
import tech.easyflow.ai.node.WorkflowNodeParser;
|
||||||
|
import tech.easyflow.common.web.exceptions.BusinessException;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
@@ -178,6 +179,40 @@ public class WorkflowRunningParameterResolverTest {
|
|||||||
Assert.assertEquals(2, ((List<?>) attachments).size());
|
Assert.assertEquals(2, ((List<?>) attachments).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件参数应允许 20MB 边界值,并拒绝超过边界的文件。
|
||||||
|
*
|
||||||
|
* @throws Exception 反射注入失败
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testNormalizeRuntimeVariablesShouldEnforceTwentyMbSingleFileLimit() throws Exception {
|
||||||
|
WorkflowRunningParameterResolver resolver = newResolver();
|
||||||
|
Map<String, Object> variables = new LinkedHashMap<>();
|
||||||
|
variables.put("attachments", fileValue(
|
||||||
|
"accepted.pdf",
|
||||||
|
"/files/accepted.pdf",
|
||||||
|
20L * 1024L * 1024L
|
||||||
|
));
|
||||||
|
|
||||||
|
Map<String, Object> normalized = resolver.normalizeRuntimeVariables(
|
||||||
|
workflowContentWithStartParameters(),
|
||||||
|
variables
|
||||||
|
);
|
||||||
|
Assert.assertEquals(1, ((List<?>) normalized.get("attachments")).size());
|
||||||
|
|
||||||
|
variables.put("attachments", fileValue(
|
||||||
|
"oversized.pdf",
|
||||||
|
"/files/oversized.pdf",
|
||||||
|
20L * 1024L * 1024L + 1L
|
||||||
|
));
|
||||||
|
try {
|
||||||
|
resolver.normalizeRuntimeVariables(workflowContentWithStartParameters(), variables);
|
||||||
|
Assert.fail("expected BusinessException");
|
||||||
|
} catch (BusinessException exception) {
|
||||||
|
Assert.assertEquals("文件参数 attachments 中单个文件不能超过 20MB", exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static WorkflowRunningParameterResolver newResolver() throws Exception {
|
private static WorkflowRunningParameterResolver newResolver() throws Exception {
|
||||||
WorkflowRunningParameterResolver resolver = new WorkflowRunningParameterResolver();
|
WorkflowRunningParameterResolver resolver = new WorkflowRunningParameterResolver();
|
||||||
ChainParser parser = ChainParser.builder()
|
ChainParser parser = ChainParser.builder()
|
||||||
|
|||||||
@@ -263,6 +263,38 @@ public class DocNodeFileContentExtractorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证文档节点允许 20MB 边界值,并拒绝超过边界的文件。
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldEnforceTwentyMbSingleFileLimit() {
|
||||||
|
DocNodeFileContentExtractor extractor = new DocNodeFileContentExtractor(
|
||||||
|
new RecordingDocumentParseBridgeService(),
|
||||||
|
new FakeFileStorageService(),
|
||||||
|
new FakeReaderManager("plain text")
|
||||||
|
);
|
||||||
|
Map<String, Object> accepted = buildFileValue(
|
||||||
|
"accepted.pdf",
|
||||||
|
"/files/accepted.pdf",
|
||||||
|
"application/pdf"
|
||||||
|
);
|
||||||
|
accepted.put("size", 20L * 1024L * 1024L);
|
||||||
|
Assert.assertEquals(1, extractor.toDocumentSourceRefs(accepted).size());
|
||||||
|
|
||||||
|
Map<String, Object> oversized = buildFileValue(
|
||||||
|
"oversized.pdf",
|
||||||
|
"/files/oversized.pdf",
|
||||||
|
"application/pdf"
|
||||||
|
);
|
||||||
|
oversized.put("size", 20L * 1024L * 1024L + 1L);
|
||||||
|
try {
|
||||||
|
extractor.toDocumentSourceRefs(oversized);
|
||||||
|
Assert.fail("expected BusinessException");
|
||||||
|
} catch (BusinessException exception) {
|
||||||
|
Assert.assertEquals("单个文件不能超过 20MB: oversized.pdf", exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, Object> buildFileValue(String fileName, String filePath, String contentType) {
|
private Map<String, Object> buildFileValue(String fileName, String filePath, String contentType) {
|
||||||
Map<String, Object> value = new HashMap<String, Object>();
|
Map<String, Object> value = new HashMap<String, Object>();
|
||||||
value.put("fileName", fileName);
|
value.put("fileName", fileName);
|
||||||
|
|||||||
@@ -179,7 +179,7 @@
|
|||||||
"answerRequired": "Answer is required",
|
"answerRequired": "Answer is required",
|
||||||
"collectionRequired": "Collection id is required",
|
"collectionRequired": "Collection id is required",
|
||||||
"imageTypeInvalid": "Only JPG/PNG/WEBP/GIF images are supported",
|
"imageTypeInvalid": "Only JPG/PNG/WEBP/GIF images are supported",
|
||||||
"imageSizeExceeded": "Image size must be less than 5MB",
|
"imageSizeExceeded": "Image size must be less than 20MB",
|
||||||
"imageUploadFailed": "Image upload failed",
|
"imageUploadFailed": "Image upload failed",
|
||||||
"import": {
|
"import": {
|
||||||
"title": "Import FAQ",
|
"title": "Import FAQ",
|
||||||
|
|||||||
@@ -179,7 +179,7 @@
|
|||||||
"answerRequired": "答案不能为空",
|
"answerRequired": "答案不能为空",
|
||||||
"collectionRequired": "知识库ID不能为空",
|
"collectionRequired": "知识库ID不能为空",
|
||||||
"imageTypeInvalid": "仅支持 JPG/PNG/WEBP/GIF 图片",
|
"imageTypeInvalid": "仅支持 JPG/PNG/WEBP/GIF 图片",
|
||||||
"imageSizeExceeded": "图片大小不能超过5MB",
|
"imageSizeExceeded": "图片大小不能超过20MB",
|
||||||
"imageUploadFailed": "图片上传失败",
|
"imageUploadFailed": "图片上传失败",
|
||||||
"import": {
|
"import": {
|
||||||
"title": "导入FAQ",
|
"title": "导入FAQ",
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['submit', 'update:modelValue']);
|
const emit = defineEmits(['submit', 'update:modelValue']);
|
||||||
const MAX_IMAGE_SIZE = 5 * 1024 * 1024;
|
const MAX_IMAGE_SIZE = 20 * 1024 * 1024;
|
||||||
const ALLOWED_IMAGE_TYPES = new Set([
|
const ALLOWED_IMAGE_TYPES = new Set([
|
||||||
'image/gif',
|
'image/gif',
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
|
|||||||
@@ -127,11 +127,21 @@ describe('workflowFileValue', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('上传前校验单文件大小限制', () => {
|
it('上传前校验单文件大小限制', () => {
|
||||||
const oversizedFile = new File([new Uint8Array(6 * 1024 * 1024)], 'large.pdf');
|
const acceptedFile = new File(
|
||||||
|
[new Uint8Array(20 * 1024 * 1024)],
|
||||||
|
'accepted.pdf',
|
||||||
|
);
|
||||||
|
const oversizedFile = new File(
|
||||||
|
[new Uint8Array(20 * 1024 * 1024 + 1)],
|
||||||
|
'large.pdf',
|
||||||
|
);
|
||||||
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
validateWorkflowFileSelection([], [oversizedFile]),
|
validateWorkflowFileSelection([], [acceptedFile]),
|
||||||
).toThrow('单个文件不能超过 5.0 MB');
|
).not.toThrow();
|
||||||
|
expect(() => validateWorkflowFileSelection([], [oversizedFile])).toThrow(
|
||||||
|
'单个文件不能超过 20.0 MB',
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('上传前校验总大小限制', () => {
|
it('上传前校验总大小限制', () => {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export interface WorkflowResourceLike {
|
|||||||
|
|
||||||
export const WORKFLOW_FILE_LIMITS = {
|
export const WORKFLOW_FILE_LIMITS = {
|
||||||
maxCount: 10,
|
maxCount: 10,
|
||||||
maxSingleSize: 5 * 1024 * 1024,
|
maxSingleSize: 20 * 1024 * 1024,
|
||||||
maxTotalSize: 50 * 1024 * 1024,
|
maxTotalSize: 50 * 1024 * 1024,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export interface WorkflowResourceLike {
|
|||||||
|
|
||||||
export const WORKFLOW_FILE_LIMITS = {
|
export const WORKFLOW_FILE_LIMITS = {
|
||||||
maxCount: 10,
|
maxCount: 10,
|
||||||
maxSingleSize: 5 * 1024 * 1024,
|
maxSingleSize: 20 * 1024 * 1024,
|
||||||
maxTotalSize: 50 * 1024 * 1024,
|
maxTotalSize: 50 * 1024 * 1024,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user