缺陷 1008567 - Word 文件解析失败
说明:原修复(commit
450de3577)于 2026-03-19 完成,随后被 commit33e568097整体撤销,commit1c1d4b957仅补回了 TOS 上传代码,预览修复未恢复。本次在 2026-03-23 重新实现,并经过 6 轮迭代修复才最终完成。
原始需求(一字不差)
缺陷标题:word-文件解析失败
状态:new | 负责人:张倩如;尹帮会 | 严重程度:未设置 | 优先级:未设置 | 创建者:小润润
创建时间:2025-12-25 14:37:49 | 修改时间:2026-03-19 12:27:46
原始描述:
[图片: https://file.tapd.cn//tfl/captures/2025-12/tapd_67139335_base64_1766644675_344.png]
评论:
[2026-03-11 14:33:34] 张倩如:
https://linkmed.tos-cn-beijing.volces.com/docs-parsed/prod{file_id}/convert.{toExt}转换成新版office会上传到tos这个位置,前端可以拉取预览↳ [2026-03-11 14:33:57] 张倩如: 仅预览,不要支持修改!!!!
图片理解
截图1 - 原始描述(1920 × 960)- Word 解析错误提示
画面内容:
- 工作台页面顶部红色错误横幅:
文档加载失败:Word 文档解析失败: Can't find end of central directory : is this a zip file ? - 左侧文件树显示多个文件
- 右侧为 AI 对话面板,正常运行
关键细节:
- 错误来自 jszip 库,
.docx本质是 ZIP 包,jszip 无法识别旧版.doc文件头 - 错误出现在文档加载阶段
截图2 - 评论附图(925 × 898)- OnlyOffice 转换失败
画面内容:
- 工作台左侧编辑区显示警告图标 + 「加载失败: Conversion failed with code: 88」+ 「重试」按钮
- 右侧 AI 面板可正常对文档内容进行问答
关键细节:
- 错误码 88 来自 X2T WASM 本地转换失败
- 文件在知识库侧可正常解析提问,说明文件本身完好
- 问题出在前端本地 X2T WASM 转换旧版 Office 格式时失败
根因分析
OnlyOfficeViewer.vue 下载原始文件后直接交给 X2T WASM 本地转换,X2T 对旧版或特殊 Word 文件兼容性不足(返回 code 88)。后端已将旧版文件转换为新版格式并上传至 TOS,前端直接拉取 TOS 转换版本即可。
实现方案
数据流
OnlyOfficeViewer.initViewer()
→ [旧版格式 doc/ppt/xls] 尝试 TOS: docs-parsed/prod{fileId}/convert.{toExt}
├─ TOS 命中 → 使用转换版本 blob
└─ TOS 未命中 → 立即展示友好提示(不走 X2T)
→ [新版格式 docx/pptx/xlsx] 跳过 TOS,走缓存/原始下载
→ openDocument(file, elementId, onReady)
→ x2tConverter.convertDocument(file) // X2T WASM 转换为 .bin
→ createEditorInstance(...) // 初始化 OnlyOffice 编辑器
→ onAppReady: asc_openDocument(bin) // 二进制注入加载
→ 1.5s 后触发 onReady 回调(注入方式不触发 onDocumentReady)
1. OnlyOfficeViewer.vue
-
toExtMap仅含旧格式:{ doc: 'docx', ppt: 'pptx', xls: 'xlsx' } - 旧格式 TOS 未命中 → 立即 throw 友好错误,不再尝试 X2T 转换
- 新格式走缓存 → 原始下载 → X2T 流程
- 错误分支:X2T 转换失败(
Conversion failed)→ 「文件解析失败,请重新上传后再试」 - 错误分支:旧版格式 TOS 未命中 → 「该文件为旧版格式(.DOC),暂不支持直接预览,请重新上传文件后再试」
2. converter.ts
-
createEditorInstance/openDocument去掉readOnly参数(文档通过二进制注入加载,无保存地址,用户无法持久化修改,不需要限制 permissions) -
editorConfig.user: { id: 'local', name: 'User' }防止用户信息为空 -
customization: { help: false, about: false }仅隐藏帮助/关于弹窗 - 移除
ElMessage.error,由调用方统一处理错误展示
3. app.js patch(三个编辑器)
getInitials null guard(documenteditor / spreadsheeteditor / presentationeditor):
// 原代码:文档内嵌修改记录作者名可能为 undefined 导致崩溃
getInitials: function(t) { return t.split(' ')... }
// 修复后
getInitials: function(t) { if (!t) return ''; return t.split(' ')... }
spellcheck null guard(presentationeditor 独有):
// 原代码:customization.features 可能未提供导致崩溃
t.isEdit && !1 !== t.customization.features.spellcheck.change
// 修复后
t.isEdit && t.customization.features && t.customization.features.spellcheck && !1 !== t.customization.features.spellcheck.change
缓存破坏:三个编辑器的 index.html 均加入:
<script>var require = { urlArgs: "v=20260324" };</script>
修改的文件
src/components/Workspace/OnlyOfficeViewer.vuesrc/utils/onlyoffice/converter.tspublic/document/web-apps/apps/documenteditor/main/app.jspublic/document/web-apps/apps/documenteditor/main/index.htmlpublic/document/web-apps/apps/spreadsheeteditor/main/app.jspublic/document/web-apps/apps/spreadsheeteditor/main/index.htmlpublic/document/web-apps/apps/presentationeditor/main/app.jspublic/document/web-apps/apps/presentationeditor/main/index.html
测试结果
-
npm run type-check通过,无 TypeScript 错误 -
npm run test:unit通过,19/19 用例全部通过(已有测试,本次无新增) - 实机测试:
.doc文件 TOS 命中时正常渲染,TOS 未命中时展示友好提示;.docx文件完整工具栏正常显示;PPT 打开无 spellcheck 崩溃
测试覆盖
本次为缺陷修复,未新增专项单元测试。已有 19 个用例全部通过。
迭代过程(6 轮)
第 1 轮:基础实现(commit 780f384bd)
首次实现 TOS 优先加载策略:
-
OnlyOfficeViewer.vue新增 TOS → 缓存 → 原始下载的三级加载顺序 -
converter.ts新增readOnly参数,permissions: { edit: false } -
asc_openDocument注入方式不触发onDocumentReady,改为 1.5s 延迟回调兜底 -
.editor-container改为position: absolute确保全屏覆盖
第 2 轮:修复 TOS 转换范围错误(commit e1eb62395)
问题:toExtMap 误将 docx/pptx/xlsx 也包含进去,导致这些新格式文件打开时也向 TOS 发起 404 请求。
根因:新格式文件本身已是最新版 Office 格式,X2T 可直接处理,不需要 TOS 转换版本。
修复:toExtMap 仅保留 { doc: 'docx', ppt: 'pptx', xls: 'xlsx' },新格式直接走缓存/原始下载。
第 3 轮:修复 getInitials 崩溃(commits b1b5db1d6 8742f3601 e455513df d4816ff2a)
问题:打开文档时报 TypeError: Cannot read properties of undefined (reading 'split'),崩溃于 app.js:20911 的 getInitials 函数。
排查过程:
- 补充
user: { id: 'local', name: 'User' }→ 无效(崩溃来自文档内嵌元数据,不是当前用户) - 加
mode: 'view'→ 无效 - 加
permissions: { review: false, comment: false }→ 无效 - 加日志确认调用链:
render:before → getPanel → setUserName → getInitials(undefined)
根因:文档内嵌的修改记录中存在作者名为 undefined 的条目,getInitials 函数未做 null 判断。
修复:
- patch 三个编辑器的
app.js:getInitials: function(t) { if (!t) return ''; ... } -
index.html加var require = { urlArgs: "v=20260323" }破除浏览器强缓存,确保 patch 生效
第 4 轮:恢复完整工具栏(commits b4c267c7d c65ca6903 55c866f2c)
问题:打开文档后工具栏大量按钮缺失,只剩「文件」「视图」两个菜单。
排查过程:
-
mode: 'view'→ 直接隐藏全部工具栏,移除后工具栏出现但仍有缺失 -
permissions: { review: false, comment: false, chat: false, protect: false }→ 每个false都会隐藏对应工具栏 tab,逐一移除 -
customization.hideRightMenu等 → 隐藏右侧面板,移除
根因:permissions 中任何权限设为 false 都会导致对应 UI 区域消失,而非仅禁用操作。
结论:文档通过 asc_openDocument 二进制注入加载,无保存地址,用户无法持久化修改。不需要从 permissions 层面限制,直接去掉所有限制恢复完整 UI 即可。
第 5 轮:修复 presentationeditor spellcheck 崩溃(commit 17297e862)
问题:打开 .ppt/.pptx 文件时报 TypeError: Cannot read properties of undefined (reading 'spellcheck'),位于 presentationeditor/app.js:52615。
根因:app.js 内部执行 t.customization.features.spellcheck.change,当外部未提供 customization.features 字段时 features 为 undefined,访问其 .spellcheck 崩溃。
修复:
- patch
presentationeditor/app.js:加 null guardt.customization.features && t.customization.features.spellcheck && - urlArgs 升至
v=20260324破缓存
第 6 轮:旧版格式打开体验优化(commits a71c8b25f 82a2c7b7d c0873bdc0 99fdbaa9f 02a7a4bb2)
问题:.doc 等旧版格式 TOS 未命中时,仍会尝试下载原文件走 X2T 转换,转换失败后弹出 文档打开失败: Conversion failed with code: 88 技术报错,体验差。
修复:
- 旧版格式 TOS 未命中时立即 throw 友好错误,不再走 X2T 转换流程
- 错误提示:「该文件为旧版格式(.DOC),暂不支持直接预览,请重新上传文件后再试」
- X2T 转换失败兜底:「文件解析失败,请重新上传后再试」
- 移除
converter.ts中的ElMessage.error,由组件统一展示错误