跳转到内容
GitHub

主题 / 后端与数据

20 文档解析与内容处理

安全、可追溯地解析和转换不可信文件:PDF、Office、图片、压缩包、HTML、邮件、音视频;OCR 与版面分析;格式转换;解析器隔离运行;结构化输出。

草稿复核于 2026-11-14 · 下次复核 2027-05-1434 条规则 · 硬约束 19 条在 GitHub 上修改

一页结论

  1. 上传的文件一律当恶意输入。 先按内容签名识别真实类型,再按类型设大小、解压比、条目数、页数、像素、时长上限,超限即停。
  2. 解析器关在笼子里跑。 独立进程或容器,非 root、默认禁网、只读根文件系统、有超时和内存上限;超时杀掉整个进程组。
  3. 文件里的「活」内容不执行。 XML 类输入关掉 DTD 和外部实体;Office 宏、PDF 脚本不执行;渲染 HTML 不加载外部资源;外部命令用参数数组,不经 shell。
  4. 原生格式先原生读。 Office 用结构库,有文字层的 PDF 直接读文字和坐标,只有扫描页才 OCR。
  5. 存原件和结构化结果,Markdown 只是派生视图。 每个块记下出处位置(页+坐标、单元格范围、幻灯片、时间码),以及源文件哈希和解析器版本。
  6. 失败要说出来。 失败、部分成功、成功分开标,不静默返回空;模型补出来的内容单独标注;没有置信度就留空。
  7. 选型靠自己的样本实测。 Docling 作通用默认;中文复杂 PDF、公式、表格拿 MinerU、Marker 一起对比,厂商榜单只作参考。
  8. 许可证分开审代码、权重、字体、编解码器。 PyMuPDF、MuPDF、Ghostscript 是 AGPL;MinerU 是 Apache-2.0 加附加条款;Marker 代码 Apache-2.0、权重有商业门槛。独立工具和随产品分发的组件要求不同,口径见 27。
  9. 离线要真断网测过才算。 模型、语言包、字体、浏览器预置进镜像,在禁网容器里跑通端到端冒烟测试。

规则

级别
检查方式
显示 34 条
FILE-001硬约束检查:自动

按文件内容签名识别真实类型再选解析器;类型与声明不符或不在允许清单里时拒绝,不凭扩展名或客户端给的 MIME 选解析器

怎么检查:测试集放入扩展名与内容不符的样本(如改名为 .pdf 的 ZIP),断言被拒绝或按真实类型处理
为什么、来源
为什么:扩展名和 MIME 都由上传方控制,按它们选解析器会把文件送进错误的、往往更脆弱的代码路径。
  • 识别工具:Apache Tika、libmagic(file 命令)。
  • OOXML、ODF、EPUB 都是 ZIP 容器,签名一样,要再看容器内的清单文件才能确定子类型。
  • 上传入口的通用校验(大小、文件名、存储位置、杀毒)见 25。
FILE-002硬约束检查:自动

解析前配置并强制执行资源上限:原始大小、解压后总大小、压缩比、条目数、嵌套层数、页数、解码后像素、音视频时长

怎么检查:配置检查确认每项上限都有值;测试集放入压缩炸弹、XML 实体炸弹、超大像素图片、超长音视频,断言在上限处停止并报错
参考值:具体数值按业务定,没有通用标准;ImageMagick 官方示例策略给出的参考是单图宽高 8K 像素、内存 512MiB、处理时间 30 秒。
为什么、来源
为什么:压缩炸弹和解码炸弹用很小的文件耗尽内存和磁盘,是解析服务最常见的拒绝服务手段。
  • 解压时边解边计数,不要先信压缩包目录里声明的大小。
  • 图片在解码前先读头部拿宽高,超过像素上限直接拒绝(Pillow 的 MAX_IMAGE_PIXELS、ImageMagick 的 policy.xml)。
  • 音视频先用 ffprobe 读时长、轨道、编码,再决定是否处理。
FILE-003硬约束检查:自动

解压压缩包时拒绝绝对路径、含 ..、符号链接和硬链接的条目,解到本任务独立的临时目录,并校验解压后的路径仍在该目录内

怎么检查:测试集放入路径穿越(Zip Slip)和符号链接样本,断言不会写出临时目录;静态扫描查找未校验路径的解压调用
为什么、来源
为什么:压缩包里的文件名由攻击者控制,不校验就能覆盖任意文件。
  • 解压后的文件用程序生成的名字保存,原文件名只作为元数据。
  • Python 的 tarfile 用 filter="data";zipfile 自己校验 os.path.realpath 结果在目标目录内。
FILE-004软指导检查:半自动

大文件流式读取或按页处理,不把整个文件一次读进内存

怎么检查:评审时查找对不可信文件的整文件读取(如 read() 不带长度);压测时看峰值内存
为什么、来源
为什么:整文件读取让内存占用随输入线性增长,单个大文件就能拖垮进程。
FILE-005硬约束检查:自动对外提供服务的后端

不可信文件的解析和转换必须在独立进程或容器里运行,并设墙钟超时、CPU、内存、临时盘和并发上限;超时杀掉整个进程组

怎么检查:部署配置检查资源上限字段;故障注入测试(死循环样本、超大样本)断言进程被杀、子进程不残留
为什么、来源
为什么:文档解析器(尤其是 C/C++ 写的 PDF、图像、Office 后端)漏洞多,放在主服务进程里,一个畸形文件就能让服务崩溃或被控制。
  • LibreOffice、Chromium、Ghostscript 都会派生子进程,只杀父进程会留下孤儿进程。
  • 容器资源上限的写法见 26。
FILE-006硬约束检查:自动对外提供服务的后端

解析进程以非 root 运行、默认禁止网络、根文件系统只读、只挂载本任务的临时目录;需要联网的例外写明原因并经批准

怎么检查:部署策略检查(如 Kubernetes 的 securityContext 与 NetworkPolicy、docker run --network none --read-only);运行时抓取网络连接,断言无外联
为什么、来源
为什么:即使解析器被攻破,也要让攻击者拿不到网络和其他任务的文件。
FILE-007硬约束检查:自动

解析和渲染过程中不主动获取外部资源(远程图片、样式表、字体、XInclude、链接文件);确需抓取用户给的 URL 时按 25 的 SSRF 规则处理

怎么检查:测试集放入引用外部地址的 HTML、SVG、DOCX、PDF,在抓包或 DNS 记录里断言无请求
为什么、来源
为什么:文档里的外部引用会把内网地址、文件内容带出去,或被用来探测内网。
  • HTML 转 PDF 用 Playwright 时,拦截所有请求,只放行预置的本地资源;不可信 HTML 关闭 JavaScript。
  • WeasyPrint 用自定义 url_fetcher 拒绝远程地址和 file://。
FILE-008硬约束检查:自动

解析 XML 类输入(XML、OOXML、ODF、SVG、XHTML、XMP)时禁用 DTD、外部实体和 XInclude

怎么检查:测试集放入 XXE 和实体炸弹样本;静态扫描(Semgrep 等)查找未加固的 XML 解析器构造
为什么、来源
为什么:XML 外部实体可以读服务器文件、发起内网请求,实体嵌套可以耗尽内存。
  • Python:用 defusedxml 包装标准库解析器;openpyxl 官方建议安装 defusedxml。
  • Java:打开 disallow-doctype-decl,并关闭外部通用实体和参数实体(写法见 OWASP XXE 速查表)。
  • .NET:XmlReaderSettings.DtdProcessing = Prohibit。
  • 用了 Office 库也要确认它底层的 XML 解析是否已加固。
FILE-009硬约束检查:半自动

不执行 Office 宏、嵌入脚本、OLE 对象,不执行 PDF 中的 JavaScript 和启动动作;LibreOffice 转换时宏安全级别设为最高

怎么检查:测试集放入带宏、带自动执行动作的样本,审计转换进程有无派生意外子进程或外联
为什么、来源
为什么:宏和嵌入的活动内容是 Office 文件最常见的攻击载体。
FILE-010硬约束检查:自动

调用外部转换工具时用参数数组传参,不经 shell;用户控制的值不拼进命令字符串,文件名用程序生成的临时名

怎么检查:Semgrep、Bandit、gosec 等规则禁止 shell=True、exec 字符串拼接
为什么、来源
为什么:文件名和参数里的特殊字符会变成命令注入。

参数可能以 - 开头时,在它前面加 --,避免被当成选项。

FILE-011硬约束检查:自动

用 ImageMagick 处理不可信图片时启用受限的 policy.xml:禁用外部委托程序,禁止读入 MSL、URL、MVG、PS、PDF、SVG、XPS 等格式

怎么检查:magick identify -list policy 输出与要求的策略对比;测试集放入 MVG、PS 伪装的图片
为什么、来源
为什么:ImageMagick 会按内容把文件交给 Ghostscript 等外部程序解释,默认配置下攻击面很大。

能用 Pillow、Sharp 或 libvips 满足需求时优先用它们,攻击面更小。

FILE-012硬约束检查:自动

用 Pandoc 处理不可信输入时加 --sandbox,不启用不可信的过滤器和 Lua 脚本

怎么检查:检查调用 Pandoc 的命令参数含 --sandbox;测试集放入含 include 指令、引用本地文件的样本
为什么、来源
为什么:Pandoc 官方说明,处理不可信输入时应使用沙箱选项,否则读写器可能读取命令行以外的文件。

沙箱只限制读写器的文件访问,不限制过滤器和 PDF 生成,这两者仍要靠进程隔离。

FILE-013软指导检查:半自动

不把不可信的 PostScript、EPS 交给 Ghostscript;确需处理时保持 SAFER 模式并在隔离环境里运行

怎么检查:评审时检查调用 Ghostscript(含经 ImageMagick、OCRmyPDF 间接调用)的路径和参数
为什么、来源
为什么:PostScript 是完整的编程语言,Ghostscript 历史上多次出现沙箱逃逸漏洞。
另见:FILE-005
FILE-014硬约束检查:半自动

HTML 的正文抽取和安全净化分两步做;要展示或回传 HTML 时必须经过专用净化库,Markdown 转换不算净化

怎么检查:测试集放入 XSS 样本,断言输出中不含可执行脚本;评审时确认净化步骤存在
为什么、来源
为什么:正文抽取只去掉导航和广告,不保证安全;Markdown 也能夹带原始 HTML。

净化库:浏览器端 DOMPurify,Python nh3,Rust ammonia。前端展示规则见 07。

来源:DOMPurify、nh3另见:07 Web 前端、25 安全
FILE-015软指导检查:半自动

邮件按 MIME 解码后,正文 HTML 先净化且不加载外链,附件按同一套类型识别和隔离流程分别处理

怎么检查:测试集放入带远程图片、嵌套附件的邮件样本
为什么、来源
为什么:邮件是多种不可信格式的容器,外链图片还会泄露「已读」信息。
FILE-016软指导检查:半自动

原生格式先原生读:Office 用结构库,有文字层的 PDF 直接读文字和坐标;先按页检测有无文字层,OCR 只用于没有文字层或文字层损坏的页或区域

怎么检查:路由逻辑的单元测试覆盖数字 PDF、扫描 PDF、混合 PDF;统计每页是否用了 OCR
为什么、来源
为什么:全部转图片再 OCR 会丢掉可复制的文字、表单对象和精度,还更慢更贵。
FILE-017软指导检查:人工

选解析器或 OCR 引擎前,用自己的真实样本(中文、扫描件、复杂表格、公式等)对比候选,不只看厂商榜单

怎么检查:选型记录里附样本构成、指标(字符错误率、表格结构、阅读顺序、耗时、峰值内存)和结果
为什么、来源
为什么:不同类型的文档排名会反转,厂商自测数据和自己的文档差别很大。

字符错误率(CER)= 编辑距离 ÷ 标注字符数;中文优先看 CER。阈值按业务风险定,不设通用数值。

FILE-018软指导检查:半自动

升级解析器、OCR 引擎或模型前,在固定样本集上与当前版本对比质量和资源指标,有退步不上线

怎么检查:CI 或发布流程里跑对比,记录新旧结果
为什么、来源
为什么:新版本不一定更准,升级常常悄悄改变阅读顺序、表格结构和坐标。
FILE-019软指导检查:半自动

保存原件和结构化中间结果;Markdown、纯文本只作派生视图,不作唯一的存储格式

怎么检查:评审存储结构,确认原件与结构化结果都有保存
为什么、来源
为什么:Markdown 会丢坐标、合并单元格和块类型,丢了就无法再精确引用或重新切块。
FILE-020硬约束检查:自动

需要引用出处的场景,每个输出块必须带可回溯的位置:页码和坐标框(注明坐标系)、工作表和单元格范围、幻灯片和形状、DOM 路径或音视频时间码

怎么检查:往返测试:按块记录的位置回原文取内容,断言与块内容一致
为什么、来源
为什么:没有精确位置,引用就无法被核对,也无法在原文上高亮或批注。
  • 不同解析器的坐标原点、单位、旋转处理不同,统一转换到一个约定的坐标系,并写在输出里。
  • 块编号由源文件哈希、规范化位置和块类型计算,重新解析后的变化用映射关联旧编号。
FILE-021软指导检查:自动

输出记录源文件 SHA-256、大小、识别出的类型,以及解析器和模型的名称、版本、配置摘要;同一输入同一配置重试时结果一致

怎么检查:用 JSON Schema 校验输出字段;同一样本重复解析,比较输出哈希
为什么、来源
为什么:解析器版本一变结果就可能变,记下这些才能复现、比较和回滚。
FILE-022硬约束检查:自动

解析结果必须显式标出状态(未尝试、失败、部分成功、成功)和警告;不得在失败时静默返回空内容

怎么检查:错误注入测试(损坏文件、加密文件、超时),断言状态和警告字段正确
为什么、来源
为什么:空结果被当成「文件里没内容」,下游检索和问答会给出错误答案且无人察觉。
FILE-023软指导检查:自动

需要保留表格结构的场景,保留合并单元格、行列顺序、表头和原始数值类型,不只存显示出来的字符串

怎么检查:含合并单元格、跨页表格、数字和日期的样本做比对测试
为什么、来源
为什么:只存字符串会丢掉数值精度和行列关系,统计和问数会出错。
FILE-024硬约束检查:自动

OCR 和语音转写结果记录引擎、语言、位置或时间码;引擎不提供置信度时字段留空,不编造数值;「用了 OCR」和「质量低」分开记

怎么检查:用 schema 校验字段;检查置信度只来自引擎原始输出
为什么、来源
为什么:编造的置信度会让下游错误地信任或丢弃内容。
FILE-025硬约束检查:半自动调用大模型的应用

由模型(视觉模型、大模型)补出或推测的内容单独标注来源,不混作从原文抽取的事实

怎么检查:输出 schema 区分「抽取」与「生成」;人工抽查生成内容是否被标注
为什么、来源
为什么:模型可能编造表格数字和文字,混进原文后无法追责也无法纠错。
FILE-026软指导检查:自动调用大模型的应用

切块时保留每块的出处位置和父级标题路径;切块策略见 24

怎么检查:每块回原文反查位置
为什么、来源
为什么:切块丢了出处,检索出来的内容就无法引用。
FILE-027软指导检查:自动

生成 Office 文件用该语言的原生库,不用改扩展名的 CSV 冒充 XLSX;公式、日期、数值类型做往返测试

怎么检查:生成后用同一库或另一种库读回,比对单元格类型和值
为什么、来源
为什么:伪装的文件在 Excel 里打开会丢精度、错日期,或弹出格式警告。
FILE-028软指导检查:半自动

LibreOffice 无头转换每个任务使用独立的用户配置目录和临时目录,任务结束后清理,不同任务不共享配置

怎么检查:检查调用参数含 -env:UserInstallation=file:///... 且路径按任务生成
为什么、来源
为什么:共享配置目录会引起锁冲突、状态串扰,一个任务的宏设置或缓存会影响别的任务。
FILE-029软指导检查:半自动

生成或转换 PDF 时预置并嵌入 CJK 字体;转换后检查页数、能否抽出文字、关键页截图对比,不只看命令返回码

怎么检查:转换流水线里跑文本抽取和截图对比测试
为什么、来源
为什么:缺字体时转换照样返回成功,但输出是方块字或空白页。

字体许可与软件许可是两回事,可分发的字体如 Noto CJK / 思源系列。

FILE-030硬约束检查:自动私有化或断网部署

要求离线部署的项目,模型权重、OCR 语言包、字体、浏览器、编解码器预置进镜像,并在禁网容器里跑通端到端冒烟测试;生产环境不在运行时从公网下载模型

怎么检查:CI 用 docker run --network none 跑解析、OCR、转换的冒烟测试;同时检查 DNS 和连接记录
为什么、来源
为什么:很多解析库第一次运行时会悄悄下载模型,镜像能启动不等于能离线工作。

关闭各库的自动下载与遥测(如 Hugging Face 的离线环境变量),模型从内部制品库加载。

FILE-031硬约束检查:人工

真实文件上传到云端 OCR 或文档识别服务前,必须完成数据分类和审批;要求断网的项目不得使用

怎么检查:评审时核对审批记录,包括数据类型、服务所在地域、保留期限
为什么、来源
为什么:文件里常有个人信息和商业机密,上传云端可能违反合同或数据出境规定。
FILE-032软指导检查:半自动

解析产生的临时文件和中间产物在任务结束或超时后删除;日志只记哈希、大小、类型,不记文件正文

怎么检查:生命周期测试检查临时目录被清空;日志抽查
为什么、来源
为什么:残留的临时文件和日志会变成没人管的敏感数据副本。
另见:25 安全
FILE-033硬约束检查:半自动

解析组件的代码、模型权重、字体、编解码器分别核对许可证;AGPL、GPL、带商业门槛或非商用条款的组件,在随产品分发或对外提供服务前按 27 审批

怎么检查:许可扫描工具找出候选,人工核对官方仓库的 LICENSE 和模型许可文件,留审批记录
为什么、来源
为什么:文档解析领域双许可、附加条款、代码与权重许可不同的情况特别多,只看代码许可会漏掉义务。
  • PyMuPDF、MuPDF、Ghostscript:AGPL-3.0 或商业许可。作为独立工具内部使用和随产品分发、对外提供网络服务,义务不同。
  • MinerU:Apache-2.0 加附加条款,月活超 1 亿或月收入超 2000 万美元需商业许可,对外提供在线服务须标明使用了 MinerU,违反则许可自动终止。
  • Marker:代码 Apache-2.0,模型权重为修改版 OpenRAIL-M,超出研究、个人和小型创业公司范围的商用需另购许可。
  • FFmpeg:默认 LGPL,启用 GPL 组件后整体按 GPL。
  • Sharp 自带的 libvips 是 LGPL-2.1。
  • 这些项目的许可在近年变过不止一次,每次升级都要重新核对。
FILE-034软指导检查:自动

解析相关的依赖、模型和字体记入台账(版本、许可、哈希),并订阅它们的安全公告和许可变更

怎么检查:SBOM 和许可扫描在 CI 里运行;依赖更新工具提醒新版本
为什么、来源
为什么:解析器漏洞多、许可常变,没有台账就不知道受不受影响。

选型

类别默认备选(何时用)不推荐
多格式结构化解析(PDF、Office、HTML 统一出结构)Docling · Docling 代码 MIT,官方模型 Apache-2.0 / CDLA-Permissive-2.0;MinerU 为 Apache-2.0 加附加条款(商业门槛、在线服务标识义务);Marker 代码 Apache-2.0、权重修改版 OpenRAIL-M;Unstructured 开源库 Apache-2.0,所用模型另查
需要版面、表格、阅读顺序和出处位置的通用场景
MinerU(中文复杂 PDF、学术论文、公式;须与 Docling、Marker 用自己的样本对比,并接受其附加许可条款)
Marker(PDF 转 Markdown/JSON、需要多种速度档位时;模型权重商用有门槛)
Unstructured(开源库)(来源杂、需要按元素分区的数据接入;高精度模式依赖较重的模型)
只抽纯文本:丢掉版面、表格和出处,事后无法补回
不经实测就定一个「最好的」解析器:不同文档类型排名会反转
文件类型识别与元数据Apache Tika · Apache-2.0
libmagic(file 命令及各语言绑定)(只要类型识别、不想引入 Java 运行时)
按扩展名判断:上传方可以随意伪造
数字 PDF 的文字和坐标pdfplumber(Python) · pdfplumber MIT;PDFBox Apache-2.0;pypdf BSD-3-Clause;pdfcpu Apache-2.0;PyMuPDF AGPL-3.0 或商业许可
Apache PDFBox(Java 项目,或需要程序式生成 PDF)
pypdf(合并、拆分、表单、元信息等页面级操作)
pdfcpu(Go 项目的 PDF 处理)
PyMuPDF(需要高性能渲染与几何操作,且已按 27 处理 AGPL 或购买商业许可)
对有文字层的 PDF 直接 OCR:比原生文字更不准、更慢
未经许可审查默认使用 PyMuPDF:AGPL-3.0,随产品分发或对外提供服务会带来开源义务
OCR(中文为主)PaddleOCR · PaddleOCR、RapidOCR、Tesseract 代码均为 Apache-2.0,模型权重另查;OCRmyPDF MPL-2.0
RapidOCR(想用 ONNX 运行时轻量部署、不装 Paddle 框架)
Tesseract(拉丁文字为主、依赖要最轻)
OCRmyPDF(给扫描 PDF 加可搜索的文字层;它会调用 Ghostscript,注意 AGPL)
用 Tesseract 单独处理中文手写和复杂表格:效果差,需要专门模型
Office 读写(Python)openpyxl、python-docx、python-pptx · 均为 MIT
XlsxWriter(只生成、不读取的大表)
把 pandas DataFrame 当格式保真工具:丢样式、公式和单元格类型
自己解压 OOXML 手写 XML:关系、命名空间、兼容性复杂,还容易引入 XXE
Office 读写(Java)Apache POI · POI Apache-2.0;FastExcel Apache-2.0
FastExcel(大批量 Excel 读写、原先用 EasyExcel 的项目迁移)
EasyExcel:官方仓库已归档,新项目不要用
Office 读写(Go)Excelize · BSD-3-Clause—
自己拼 OOXML:公式、样式、兼容性成本高
Office 读写(Node)ExcelJS(Excel)、PptxGenJS(生成 PPT) · ExcelJS MIT;PptxGenJS MIT;SheetJS 社区版 Apache-2.0
SheetJS 社区版(要读很多种老旧表格格式;从官方渠道安装并锁定哈希)
npm 上的 xlsx 包:官方已不在 npm 发布新版,npm 上的版本过旧
Office 转 PDF 或图片LibreOffice 无头模式(作为独立进程调用) · MPL-2.0(含其他开源组件)—
承诺转换结果与 Office 排版完全一致:字体和排版引擎不同,必然有差异
标记类格式互转(Markdown、HTML、DOCX、EPUB 等)Pandoc(作为独立命令行工具调用,加 --sandbox) · GPL-2.0 或更高版本—
把 Pandoc 作为库链接进闭源产品分发:GPL 许可要求随之开源
HTML 转 PDFPlaywright + Chromium · Playwright Apache-2.0;WeasyPrint BSD-3-Clause
WeasyPrint(不需要 JavaScript、以打印样式排版的服务端报表)
截图拼成 PDF:文字不可搜索、不可复制,也没有无障碍信息
网页正文抽取trafilatura(Python)、Mozilla Readability(JS) · 均为 Apache-2.0—
用正则剥 HTML:不可靠,也不安全
把整页 HTML 塞给下游:导航、广告干扰内容
HTML 安全净化DOMPurify(浏览器/JS)、nh3(Python)、ammonia(Rust) · DOMPurify Apache-2.0 或 MPL-2.0;nh3 MIT;ammonia MIT 或 Apache-2.0—
把 Markdown 转换当净化:Markdown 不是安全边界
XML 安全解析Python 用 defusedxml;Java 和 .NET 按 OWASP XXE 速查表关闭 DTD · defusedxml 为 PSF-2.0——
图片解码与转换Pillow(Python)、Sharp(Node) · Pillow MIT-CMU;Sharp Apache-2.0(自带 libvips,LGPL-2.1);libvips LGPL-2.1
libvips(大图、低内存的批量处理)
ImageMagick(需要它独有的格式;必须配受限 policy.xml(见 FILE-011))
把高分辨率原图直接交给模型:成本高,也可能触发解码炸弹
音视频探测与转码FFmpeg + ffprobe(独立进程调用) · FFmpeg 默认 LGPL-2.1 或更高,启用 GPL 组件后为 GPL;faster-whisper、whisper.cpp 代码 MIT,模型权重另查
faster-whisper 或 whisper.cpp(离线语音转写,两者用自己的样本对比)
默认把录音上传外部转写接口:数据边界不清,见 FILE-031
间接引入的 PDF 和图像后端(许可提示)引入前查清依赖链里有没有 Ghostscript、MuPDF、Poppler、libvips · Ghostscript AGPL-3.0(另有商业许可);MuPDF AGPL-3.0(另有商业许可);libvips LGPL-2.1;Poppler 待核实——
多格式结构化解析(PDF、Office、HTML 统一出结构)
Docling · Docling 代码 MIT,官方模型 Apache-2.0 / CDLA-Permissive-2.0;MinerU 为 Apache-2.0 加附加条款(商业门槛、在线服务标识义务);Marker 代码 Apache-2.0、权重修改版 OpenRAIL-M;Unstructured 开源库 Apache-2.0,所用模型另查
备选:MinerU(中文复杂 PDF、学术论文、公式;须与 Docling、Marker 用自己的样本对比,并接受其附加许可条款);Marker(PDF 转 Markdown/JSON、需要多种速度档位时;模型权重商用有门槛);Unstructured(开源库)(来源杂、需要按元素分区的数据接入;高精度模式依赖较重的模型)
不推荐:只抽纯文本;不经实测就定一个「最好的」解析器
文件类型识别与元数据
Apache Tika · Apache-2.0
备选:libmagic(file 命令及各语言绑定)(只要类型识别、不想引入 Java 运行时)
不推荐:按扩展名判断
数字 PDF 的文字和坐标
pdfplumber(Python) · pdfplumber MIT;PDFBox Apache-2.0;pypdf BSD-3-Clause;pdfcpu Apache-2.0;PyMuPDF AGPL-3.0 或商业许可
备选:Apache PDFBox(Java 项目,或需要程序式生成 PDF);pypdf(合并、拆分、表单、元信息等页面级操作);pdfcpu(Go 项目的 PDF 处理);PyMuPDF(需要高性能渲染与几何操作,且已按 27 处理 AGPL 或购买商业许可)
不推荐:对有文字层的 PDF 直接 OCR;未经许可审查默认使用 PyMuPDF
OCR(中文为主)
PaddleOCR · PaddleOCR、RapidOCR、Tesseract 代码均为 Apache-2.0,模型权重另查;OCRmyPDF MPL-2.0
备选:RapidOCR(想用 ONNX 运行时轻量部署、不装 Paddle 框架);Tesseract(拉丁文字为主、依赖要最轻);OCRmyPDF(给扫描 PDF 加可搜索的文字层;它会调用 Ghostscript,注意 AGPL)
不推荐:用 Tesseract 单独处理中文手写和复杂表格
Office 读写(Python)
openpyxl、python-docx、python-pptx · 均为 MIT
备选:XlsxWriter(只生成、不读取的大表)
不推荐:把 pandas DataFrame 当格式保真工具;自己解压 OOXML 手写 XML
Office 读写(Java)
Apache POI · POI Apache-2.0;FastExcel Apache-2.0
备选:FastExcel(大批量 Excel 读写、原先用 EasyExcel 的项目迁移)
不推荐:EasyExcel
Office 读写(Go)
Excelize · BSD-3-Clause
不推荐:自己拼 OOXML
Office 读写(Node)
ExcelJS(Excel)、PptxGenJS(生成 PPT) · ExcelJS MIT;PptxGenJS MIT;SheetJS 社区版 Apache-2.0
备选:SheetJS 社区版(要读很多种老旧表格格式;从官方渠道安装并锁定哈希)
不推荐:npm 上的 xlsx 包
Office 转 PDF 或图片
LibreOffice 无头模式(作为独立进程调用) · MPL-2.0(含其他开源组件)
不推荐:承诺转换结果与 Office 排版完全一致
标记类格式互转(Markdown、HTML、DOCX、EPUB 等)
Pandoc(作为独立命令行工具调用,加 --sandbox) · GPL-2.0 或更高版本
不推荐:把 Pandoc 作为库链接进闭源产品分发
HTML 转 PDF
Playwright + Chromium · Playwright Apache-2.0;WeasyPrint BSD-3-Clause
备选:WeasyPrint(不需要 JavaScript、以打印样式排版的服务端报表)
不推荐:截图拼成 PDF
网页正文抽取
trafilatura(Python)、Mozilla Readability(JS) · 均为 Apache-2.0
不推荐:用正则剥 HTML;把整页 HTML 塞给下游
HTML 安全净化
DOMPurify(浏览器/JS)、nh3(Python)、ammonia(Rust) · DOMPurify Apache-2.0 或 MPL-2.0;nh3 MIT;ammonia MIT 或 Apache-2.0
不推荐:把 Markdown 转换当净化
XML 安全解析
Python 用 defusedxml;Java 和 .NET 按 OWASP XXE 速查表关闭 DTD · defusedxml 为 PSF-2.0
图片解码与转换
Pillow(Python)、Sharp(Node) · Pillow MIT-CMU;Sharp Apache-2.0(自带 libvips,LGPL-2.1);libvips LGPL-2.1
备选:libvips(大图、低内存的批量处理);ImageMagick(需要它独有的格式;必须配受限 policy.xml(见 FILE-011))
不推荐:把高分辨率原图直接交给模型
音视频探测与转码
FFmpeg + ffprobe(独立进程调用) · FFmpeg 默认 LGPL-2.1 或更高,启用 GPL 组件后为 GPL;faster-whisper、whisper.cpp 代码 MIT,模型权重另查
备选:faster-whisper 或 whisper.cpp(离线语音转写,两者用自己的样本对比)
不推荐:默认把录音上传外部转写接口
间接引入的 PDF 和图像后端(许可提示)
引入前查清依赖链里有没有 Ghostscript、MuPDF、Poppler、libvips · Ghostscript AGPL-3.0(另有商业许可);MuPDF AGPL-3.0(另有商业许可);libvips LGPL-2.1;Poppler 待核实
  • 多格式结构化解析(PDF、Office、HTML 统一出结构):「开源库」和同名的商业托管服务是两回事;私有化部署要预置模型。
  • 文件类型识别与元数据:Tika 对复杂表格和公式不够,不宜当唯一的解析引擎。
  • OCR(中文为主):默认在 CPU 上能跑,GPU 只作性能扩展。
  • Office 读写(Python):openpyxl 官方建议同时安装 defusedxml。
  • Office 读写(Node):ExcelJS 发布频率低,复杂需求可改为调用隔离的 Python 或 Java 解析服务。
  • HTML 转 PDF:需预置浏览器和 CJK 字体,并拦截外部请求(见 FILE-007)。
  • 网页正文抽取:抽取后仍需净化(见 FILE-014)。
  • XML 安全解析:defusedxml 最近一次提交在 2024 年,更新慢但仍可用。
  • 间接引入的 PDF 和图像后端(许可提示):OCRmyPDF、ImageMagick、部分 PDF 转图片工具会在背后调用这些后端,许可义务按实际分发方式判断,口径见 27。

常见错误

错误做法:按扩展名决定用哪个解析器
应该:按内容签名识别真实类型,类型不符就拒绝
错误做法:在主服务进程里直接解析用户上传的 PDF 和 Office
应该:放进独立进程或容器,限资源、禁网、设超时
错误做法:所有文件一律转图片再 OCR
应该:原生格式先原生读,OCR 按页按需启用
错误做法:只存 Markdown 或纯文本
应该:存原件和带位置的结构化结果,Markdown 只作派生视图
错误做法:解析失败时返回空字符串
应该:标明失败或部分成功,并附警告
错误做法:引用只记文件名和页码
应该:记源文件哈希和页码加坐标、单元格范围或时间码
错误做法:用 shell 字符串拼 FFmpeg 或 soffice 命令
应该:参数数组调用,文件名用程序生成的临时名
错误做法:多个转换任务共用一个 LibreOffice 配置目录
应该:每个任务独立配置目录,用完删除
错误做法:PDF 转换只看命令返回 0
应该:检查页数、文字可抽取、关键页截图
错误做法:镜像能启动就当作支持离线
应该:在禁网容器里跑通端到端冒烟测试
错误做法:只看代码许可就认定「开源可商用」
应该:代码、权重、字体、编解码器分别核对
错误做法:用模型补全解析失败的内容且不标注
应该:标为生成内容,或交人工复核

待确认

下面的内容还没核实或待补调研,不要当成规则执行。

  • Poppler 的许可(据了解为 GPL-2.0 或 GPL-3.0),本次未从官方仓库核实
  • PaddleOCR、RapidOCR 各模型权重的许可未逐个核实;Unstructured 高精度模式所用模型的许可未核实
  • Marker 代码许可于 2026-07 由 GPL-3.0 改为 Apache-2.0,MinerU 在 2026 年 3–4 月间多次在 AGPL-3.0 与 Apache-2.0 之间变更、最终为 Apache-2.0 加附加条款;两者变化频繁,每次升级须重新核对 LICENSE
  • 程序式 PDF 生成的选型(ReportLab、PDFKit、pdf-lib、QuestPDF 等)及其许可、维护状态未核实,暂未写入选型
  • LibreOffice 各发布分支的停止支持日期未核实
  • 待补调研:Ghostscript、Poppler、MuPDF、libvips 被间接引入时的许可风险与替代方案
  • 待补调研:OFD、WPS 专有格式、老式 .doc/.xls/.ppt、带电子签章的 PDF 的解析与转换
  • 待补调研:Go、Rust 的 Office 写入和 PDF 处理库;.NET 的 Office 与 PDF 库许可(QuestPDF、ImageSharp 等)
  • 待补调研:国产 CPU 与操作系统(ARM64、龙芯、统信、麒麟)上解析器、OCR、LibreOffice、Chromium 的可用性
  • 待补调研:云端 OCR 与文档识别服务(国内外)的数据驻留和私有化部署选项

来源

  1. S01 OWASP File Upload Cheat Sheet · OWASP · 标准 · 核实于 2026-11-14
  2. S02 OWASP XML External Entity Prevention Cheat Sheet · OWASP · 标准 · 核实于 2026-11-14
  3. S03 Macros from the internet are blocked by default in Office · Microsoft · 官方文档 · 核实于 2026-11-14
  4. S04 Docling · Docling Project · 官方文档 · 核实于 2026-11-14
  5. S05 Docling models · Docling Project · 官方文档 · 核实于 2026-11-14
  6. S06 MinerU Open Source License · OpenDataLab · 官方文档 · 2026-04-17 · 核实于 2026-11-14
  7. S07 Marker · Datalab · 官方文档 · 核实于 2026-11-14
  8. S08 Marker MODEL_LICENSE · Datalab · 官方文档 · 核实于 2026-11-14
  9. S09 PyMuPDF · Artifex · 官方文档 · 核实于 2026-11-14
  10. S10 pdfplumber · pdfplumber · 官方文档 · 核实于 2026-11-14
  11. S11 PaddleOCR · PaddlePaddle · 官方文档 · 核实于 2026-11-14
  12. S12 RapidOCR · RapidAI · 官方文档 · 核实于 2026-11-14
  13. S13 Tesseract OCR · Tesseract OCR · 官方文档 · 核实于 2026-11-14
  14. S14 Apache Tika · Apache Software Foundation · 官方文档 · 核实于 2026-11-14
  15. S15 Unstructured · Unstructured · 官方文档 · 核实于 2026-11-14
  16. S16 LibreOffice Licenses · The Document Foundation · 官方文档 · 核实于 2026-11-14
  17. S17 Pandoc User's Guide(--sandbox) · Pandoc · 官方文档 · 核实于 2026-11-14
  18. S18 FFmpeg License and Legal Considerations · FFmpeg · 官方文档 · 核实于 2026-11-14
  19. S19 Ghostscript(ghostpdl 仓库 LICENSE) · Artifex · 官方文档 · 核实于 2026-11-14
  20. S20 MuPDF · Artifex · 官方文档 · 核实于 2026-11-14
  21. S21 libvips · libvips · 官方文档 · 核实于 2026-11-14
  22. S22 defusedxml · defusedxml · 官方文档 · 核实于 2026-11-14
  23. S23 ImageMagick Security Policy · ImageMagick · 官方文档 · 核实于 2026-11-14
  24. S24 Pillow: Image module(MAX_IMAGE_PIXELS) · Pillow · 官方文档 · 核实于 2026-11-14
  25. S25 EasyExcel(已归档) · Alibaba · 官方文档 · 核实于 2026-11-14
  26. S26 FastExcel · FastExcel · 官方文档 · 核实于 2026-11-14
  27. S27 SheetJS: Node.js installation · SheetJS · 官方文档 · 核实于 2026-11-14
  28. S28 Playwright: Page API · Microsoft · 官方文档 · 核实于 2026-11-14
  29. S29 Docling Documentation · Docling Project · 官方文档 · 核实于 2026-11-14
  30. S30 CWE-22: Path Traversal · MITRE · 标准 · 核实于 2026-11-14
  31. S31 Excelize · Excelize · 官方文档 · 核实于 2026-11-14
  32. S32 Apache POI · Apache Software Foundation · 官方文档 · 核实于 2026-11-14
  33. S33 trafilatura · trafilatura · 官方文档 · 核实于 2026-11-14
  34. S34 Mozilla Readability · Mozilla · 官方文档 · 核实于 2026-11-14
  35. S35 DOMPurify · Cure53 · 官方文档 · 核实于 2026-11-14
  36. S36 nh3 · nh3 · 官方文档 · 核实于 2026-11-14
  37. S37 sharp · sharp · 官方文档 · 核实于 2026-11-14
  38. S38 WeasyPrint · Kozea · 官方文档 · 核实于 2026-11-14
  39. S39 pypdf · py-pdf · 官方文档 · 核实于 2026-11-14
  40. S40 OCRmyPDF · OCRmyPDF · 官方文档 · 核实于 2026-11-14
  41. S41 faster-whisper · SYSTRAN · 官方文档 · 核实于 2026-11-14
  42. S42 whisper.cpp · ggml-org · 官方文档 · 核实于 2026-11-14
  43. S43 openpyxl documentation · openpyxl · 官方文档 · 核实于 2026-11-14