Files
EasyFlow/easyflow-ui-admin/app/src/utils/document-parse-file-policy.ts
陈子默 c3673ece46 fix: 统一文档解析文件格式校验
- 统一知识库和工作流支持格式并增加前后端上传拦截

- 拒绝 XLS 与伪装 XLSX,避免空内容解析成功
2026-09-02 19:15:01 +08:00

84 lines
2.2 KiB
TypeScript

import { $t } from '#/locales';
const OLE2_SIGNATURE = [208, 207, 17, 224, 161, 177, 26, 225];
const supportedExtensions = Object.freeze([
'txt',
'pdf',
'docx',
'md',
'pptx',
'xlsx',
'csv',
]);
const supportedExtensionSet = new Set(supportedExtensions);
const supportedTypeLabel = supportedExtensions
.map((extension) => extension.toUpperCase())
.join('、');
function extensionOf(fileName: string) {
const normalizedName = String(fileName || '')
.trim()
.toLowerCase();
const dotIndex = normalizedName.lastIndexOf('.');
return dotIndex === -1 ? '' : normalizedName.slice(dotIndex + 1);
}
function supports(fileName: string) {
return supportedExtensionSet.has(extensionOf(fileName));
}
async function validateFiles(files: File[]) {
for (const file of files) {
const extension = extensionOf(file.name);
if (!supportedExtensionSet.has(extension)) {
throw new Error(
$t('message.upload.unsupportedFileType', {
fileName: file.name,
types: supportedTypeLabel,
}),
);
}
if (extension !== 'xlsx') {
continue;
}
const prefix = new Uint8Array(await file.slice(0, 8).arrayBuffer());
if (hasZipSignature(prefix)) {
continue;
}
const messageKey = startsWith(prefix, OLE2_SIGNATURE)
? 'message.upload.legacyExcelAsXlsx'
: 'message.upload.invalidXlsx';
throw new Error($t(messageKey, { fileName: file.name }));
}
}
/**
* 知识库导入与工作流文档解析共用的文件格式策略。
*/
export const DocumentParseFilePolicy = Object.freeze({
accept: supportedExtensions.map((extension) => `.${extension}`).join(','),
supportedExtensions,
supportedTypeLabel,
supports,
validateFiles,
});
function hasZipSignature(prefix: Uint8Array) {
return (
prefix.length >= 4 &&
prefix[0] === 80 &&
prefix[1] === 75 &&
((prefix[2] === 3 && prefix[3] === 4) ||
(prefix[2] === 5 && prefix[3] === 6) ||
(prefix[2] === 7 && prefix[3] === 8))
);
}
function startsWith(prefix: Uint8Array, signature: number[]) {
return (
prefix.length >= signature.length &&
signature.every((value, index) => prefix[index] === value)
);
}