fix: 统一文档解析文件格式校验
- 统一知识库和工作流支持格式并增加前后端上传拦截 - 拒绝 XLS 与伪装 XLSX,避免空内容解析成功
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import ImportKnowledgeFileContainer from './ImportKnowledgeFileContainer.vue';
|
||||
|
||||
vi.mock('@easyflow/hooks', () => ({
|
||||
useAppConfig: () => ({ apiURL: '' }),
|
||||
}));
|
||||
|
||||
vi.mock('@easyflow/locales', () => ({
|
||||
$t: (key: string, params?: Record<string, string>) =>
|
||||
key === 'documentCollection.importDoc.batchUploadDescription'
|
||||
? `支持 ${params?.types} 文件`
|
||||
: key,
|
||||
}));
|
||||
|
||||
vi.mock('@easyflow/stores', () => ({
|
||||
useAccessStore: () => ({ accessToken: 'test-token' }),
|
||||
}));
|
||||
|
||||
vi.mock('#/api/request', () => ({ api: {} }));
|
||||
|
||||
vi.mock('#/locales', () => ({
|
||||
$t: (key: string) => key,
|
||||
}));
|
||||
|
||||
vi.mock('element-plus/es/components/table-v2/index.mjs', () => ({
|
||||
ElAutoResizer: { template: '<div><slot :height="300" :width="600" /></div>' },
|
||||
ElTableV2: { template: '<div />' },
|
||||
}));
|
||||
|
||||
vi.mock('element-plus/es/components/table-v2/style/css.mjs', () => ({}));
|
||||
|
||||
describe('import knowledge file container', () => {
|
||||
it('uses the shared document formats for file and folder selection', () => {
|
||||
const wrapper = shallowMount(ImportKnowledgeFileContainer, {
|
||||
props: { batchMode: true },
|
||||
});
|
||||
const inputs = wrapper.findAll('input[type="file"]');
|
||||
|
||||
expect(inputs).toHaveLength(2);
|
||||
for (const input of inputs) {
|
||||
expect(input.attributes('accept')).toBe(
|
||||
'.txt,.pdf,.docx,.md,.pptx,.xlsx,.csv',
|
||||
);
|
||||
}
|
||||
expect(wrapper.text()).toContain(
|
||||
'支持 TXT、PDF、DOCX、MD、PPTX、XLSX、CSV 文件',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
import { formatFileSize } from '#/api/common/file';
|
||||
import { api } from '#/api/request';
|
||||
import DragFileUpload from '#/components/upload/DragFileUpload.vue';
|
||||
import { DocumentParseFilePolicy } from '#/utils/document-parse-file-policy';
|
||||
|
||||
import { resolveDocumentUploadResponse } from './document-import-upload-response';
|
||||
|
||||
@@ -80,16 +81,6 @@ const emit = defineEmits<{
|
||||
const MAX_FILE_COUNT = 2000;
|
||||
const MAX_FILE_SIZE_BYTES = 100 * 1024 * 1024;
|
||||
const MAX_TOTAL_SIZE_BYTES = 1024 * 1024 * 1024;
|
||||
const SUPPORTED_EXTENSIONS = new Set([
|
||||
'csv',
|
||||
'docx',
|
||||
'md',
|
||||
'pdf',
|
||||
'pptx',
|
||||
'txt',
|
||||
'xlsx',
|
||||
]);
|
||||
|
||||
const fileData = ref<LegacyFileInfo[]>([]);
|
||||
const filesPath = ref<any[]>([]);
|
||||
const dragUploadRef = ref<InstanceType<typeof DragFileUpload>>();
|
||||
@@ -331,8 +322,7 @@ async function prepareBatch(files: File[]) {
|
||||
ignoredCount++;
|
||||
continue;
|
||||
}
|
||||
const extension = file.name.split('.').pop()?.toLowerCase() || '';
|
||||
if (!SUPPORTED_EXTENSIONS.has(extension)) {
|
||||
if (!DocumentParseFilePolicy.supports(file.name)) {
|
||||
ignoredCount++;
|
||||
continue;
|
||||
}
|
||||
@@ -344,7 +334,11 @@ async function prepareBatch(files: File[]) {
|
||||
accepted.push(file);
|
||||
}
|
||||
if (accepted.length === 0) {
|
||||
ElMessage.warning($t('documentCollection.importDoc.noSupportedFiles'));
|
||||
ElMessage.warning(
|
||||
$t('documentCollection.importDoc.noSupportedFiles', {
|
||||
types: DocumentParseFilePolicy.supportedTypeLabel,
|
||||
}),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (accepted.length > MAX_FILE_COUNT) {
|
||||
@@ -356,7 +350,17 @@ async function prepareBatch(files: File[]) {
|
||||
return;
|
||||
}
|
||||
if (ignoredCount > 0) {
|
||||
ElMessage.info($t('documentCollection.importDoc.unsupportedSkipped'));
|
||||
ElMessage.info(
|
||||
$t('documentCollection.importDoc.unsupportedSkipped', {
|
||||
types: DocumentParseFilePolicy.supportedTypeLabel,
|
||||
}),
|
||||
);
|
||||
}
|
||||
try {
|
||||
await DocumentParseFilePolicy.validateFiles(accepted);
|
||||
} catch (error: any) {
|
||||
ElMessage.warning(error?.message || $t('message.notSupported'));
|
||||
return;
|
||||
}
|
||||
|
||||
batchFiles.value = await Promise.all(
|
||||
@@ -538,7 +542,7 @@ async function createClientFileKey(relativePath: string) {
|
||||
class="native-file-input"
|
||||
type="file"
|
||||
multiple
|
||||
accept=".txt,.pdf,.docx,.md,.pptx,.xlsx,.csv"
|
||||
:accept="DocumentParseFilePolicy.accept"
|
||||
@change="handleNativeSelection"
|
||||
/>
|
||||
<input
|
||||
@@ -546,6 +550,7 @@ async function createClientFileKey(relativePath: string) {
|
||||
class="native-file-input"
|
||||
type="file"
|
||||
multiple
|
||||
:accept="DocumentParseFilePolicy.accept"
|
||||
webkitdirectory
|
||||
@change="handleNativeSelection"
|
||||
/>
|
||||
@@ -564,7 +569,11 @@ async function createClientFileKey(relativePath: string) {
|
||||
{{ $t('documentCollection.importDoc.batchUploadTitle') }}
|
||||
</div>
|
||||
<div class="batch-drop-zone__description">
|
||||
{{ $t('documentCollection.importDoc.batchUploadDescription') }}
|
||||
{{
|
||||
$t('documentCollection.importDoc.batchUploadDescription', {
|
||||
types: DocumentParseFilePolicy.supportedTypeLabel,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<ElButton
|
||||
:icon="FolderOpened"
|
||||
|
||||
Reference in New Issue
Block a user