Skip to content

feat(dataset): 原生支持 OFD 文档解析与解析失败诊断错误码 - #7806

Open
sunjiaqi52777 wants to merge 3 commits into
labring:mainfrom
sunjiaqi52777:feat/kb-ofd-wps-diagnosis-codes
Open

sunjiaqi52777 wants to merge 3 commits into
labring:mainfrom
sunjiaqi52777:feat/kb-ofd-wps-diagnosis-codes

Conversation

@sunjiaqi52777

Copy link
Copy Markdown
Contributor
  • .ofd 加入内置文档扩展名白名单,前端上传与后端校验自动放行
  • worker 新增 OFD 解析器:zip + XML 提取 TextObject/TextCode,按版面启发式(坐标分行、字号识别标题、过滤页码)输出 Markdown;按 localName 匹配节点以兼容各厂商命名空间,拒绝加密文件
  • 解析分派:已配置 sangfor 外部解析服务且扩展名命中时优先走服务,否则回落内置解析器,上层契约不变
  • 新增 6 个解析失败诊断错误码(四语种 i18n);sangfor 与 anydoc 的失败统一映射为诊断码 UserError,原始错误信息只进日志

@c121914yu c121914yu self-assigned this Sep 22, 2026

Copy link
Copy Markdown
Collaborator

建议这里超过 2000 页时直接拒绝解析,而不是 slice(0, MAX_PAGE_COUNT) 后继续返回成功结果。当前行为会静默丢弃第 2001 页及之后的内容,最终写入不完整的知识库数据。请改为抛出明确的解析诊断错误(例如 officeConversionFailed 或新增专用错误码),并补充超过 2000 页的测试,验证不会返回部分 Markdown。

Copy link
Copy Markdown
Collaborator

补充几个需要合并前处理的风险点:

  1. [高] packages/service/worker/readFile/extension/ofd.ts:275-295 使用 JSZip.loadAsynccontentFile.async('string'),没有 ZIP entry 数量、总解压大小或单个 XML 大小上限。压缩文件大小不能约束解压后的内存,恶意 ZIP bomb/超大 XML 可能拖垮 worker;建议增加预检或流式解压限制,并覆盖资源边界测试。

  2. [高] ofd.ts:252-268, 290-296 对 XML parse error 没有显式校验,缺失 Content.xml 时直接 continue,缺失 Pages 也会返回空文本。损坏或不完整 OFD 可能被当作成功,写入空/部分知识库内容;建议缺失声明页面资源或 XML 无法解析时统一返回 invalidParseFile,并补充对应测试。

  3. [中] ofd.ts:134-149 的基准字号算法在“正文是最小字号且出现次数最多、文档没有页码”的正常文档中会把标题字号当成基准,导致标题无法识别。建议增加无页码真实样本测试并调整启发式。

  4. 当前 OFD 测试主要是人工构造 XML,worker integration 测试未实际执行;建议补充真实 WPS/厂商 OFD 样本,以及 malformed XML、缺页、CDATA 和 worker 分派端到端测试。

Copy link
Copy Markdown
Collaborator

关于 ZIP/XML 资源风险,建议按仓库现有 Office 解析器的方式落地,参考 packages/service/worker/readFile/parseOffice.ts:56-160packages/service/worker/readFile/extension/xlsxPreflight.ts:462-583

  • ofd.ts:275-295JSZip.loadAsync 改为已有依赖 yauzlfromBuffer(buffer, { lazyEntries: true, validateEntrySizes: true, strictFileNames: true })
  • 增加三层限制:ZIP entry 数量、单个 XML 解压大小、所有待解析 XML 的累计解压大小。初始值可以先与 PPTX 对齐,例如 maxEntries=10000、单文件 10 MiB、累计 100 MiB,再用真实 OFD 样本校准。
  • 只读取 OFD.xmlDocument.xml 和页面 Content.xml,通过 openReadStream 流式读取;entry 的 uncompressedSize 只能作为前置检查,仍需在 data chunk 中累计真实解压字节数,超限立即销毁 stream 并返回解析诊断错误。
  • 读取失败时确保 zip.close(),避免异常路径留下资源;不要把整个 ZIP 或所有页面 XML 一次性 materialize 到内存。
  • 补充 entry 数量超限、单 XML 超限、累计 XML 超限和损坏 ZIP 的单测。当前 service 已有 yauzl,不需要新增依赖。

这样可以同时防 ZIP bomb、伪造解压大小和超大页面 XML;现有的 2000 页限制则继续单独处理为直接拒绝。

sunjiaqi52777 and others added 3 commits September 22, 2026 15:37
- add .ofd to the built-in document extension whitelist
- add worker OFD parser (zip+xml text extraction with layout heuristics:
  Y grouping, title detection, page-number filtering); match elements by
  localName for vendor namespace compatibility; reject encrypted files;
  unknown failures fall back to the shared parse-failure diagnosis code

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- register 6 parse failure codes in CommonErrEnum with 4-locale i18n
- map Sangfor provider failures to UserError statusText (raw details
  log-only) and anydoc ConvertErrorCode to diagnosis codes; unknown
  codes fall back to pdfParseFailed instead of leaking raw errors
- map docx mammoth failures to docxConversionFailed
- unify unknown local pipeline failures to UserError(pdfParseFailed);
  worker-mapped diagnosis errors pass through untouched

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Replace JSZip full-materialize with yauzl streaming and enforce entry
  count / per-XML / cumulative XML size limits (align with parseOffice)
- Reject when declared pages exceed limit instead of silently truncating
- Map missing page resources and malformed XML to invalidParseFile
- Only demote base font size when page-number evidence exists, so titles
  in no-page-number documents are recognized
- Keep mapped diagnosis codes across the worker boundary: worker errors
  lose the Error subclass and its `name`, so match the preserved `message`
  against the accepted status-text set instead of checking `error.name`
- Drop the docx-specific failure code so docx keeps its original
  "convert to PDF" hint
- Align sangfor provider tests with parse diagnosis codes
- Dedupe imports in read/utils.ts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@sunjiaqi52777
sunjiaqi52777 force-pushed the feat/kb-ofd-wps-diagnosis-codes branch from 1024d56 to 16b8276 Compare September 22, 2026 11:54

Copy link
Copy Markdown
Collaborator

感谢贡献!关于解析错误处理有两点小建议:

  1. 清理未使用的 docx 错误码与语言包残留
    在最新提交中已保留 docx 原有的转 PDF 提示,目前以下几处定义已无引用,建议同步清理:

    • packages/global/common/error/code/common.ts: CommonErrEnum.docxParseInvalidCommonErrEnum.docxConversionFailed 及对应 datasetErr 数组项
    • packages/service/thirdProvider/sangfor/index.ts: ACCEPTED_PARSE_STATUS_TEXTS 白名单中的对应项
    • packages/web/i18n/{en, zh-CN, zh-Hant, ko-KR}/file.json: docx_parse_invaliddocx_conversion_failed 词条
  2. 注意本地文件解析异常的兜底范围
    packages/service/common/file/read/utils.ts (L408-L420) 中,顶层对非白名单异常一律兜底抛出 CommonErrEnum.pdfParseFailed
    这会导致 docx 抛出的 Can not read doc file, please convert to PDF(以及其他本地常规文件原有的错误提示)被抹平为通用的“文档解析失败,请稍后重试”,用户丢失了转 PDF 的引导。建议将该兜底逻辑仅约束在 OFD 或外部增强解析路径,避免影响常规本地文档的原有提示。

@sunjiaqi52777

Copy link
Copy Markdown
Contributor Author

@c121914yu 余总,docxParseInvalid/docxConversionFailed 这两个码是 sangfor外部解析服务契约,外部服务解析 docx 失败时可能返回。删除后服务返回这两个码会兜底成 pdfParseFailed,麻烦看下能否保留

@c121914yu

c121914yu commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator

@c121914yu 余总,docxParseInvalid/docxConversionFailed 这两个码是 sangfor外部解析服务契约,外部服务解析 docx 失败时可能返回。删除后服务返回这两个码会兜底成 pdfParseFailed,麻烦看下能否保留

那加个注释

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants