1008567-Word文件预览TOS修复重新实现-6轮迭代.md 9.59 KB

缺陷 1008567 - Word 文件解析失败

说明:原修复(commit 450de3577)于 2026-03-19 完成,随后被 commit 33e568097 整体撤销,commit 1c1d4b957 仅补回了 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.vue
  • src/utils/onlyoffice/converter.ts
  • public/document/web-apps/apps/documenteditor/main/app.js
  • public/document/web-apps/apps/documenteditor/main/index.html
  • public/document/web-apps/apps/spreadsheeteditor/main/app.js
  • public/document/web-apps/apps/spreadsheeteditor/main/index.html
  • public/document/web-apps/apps/presentationeditor/main/app.js
  • public/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:20911getInitials 函数。

排查过程

  1. 补充 user: { id: 'local', name: 'User' } → 无效(崩溃来自文档内嵌元数据,不是当前用户)
  2. mode: 'view' → 无效
  3. permissions: { review: false, comment: false } → 无效
  4. 加日志确认调用链:render:before → getPanel → setUserName → getInitials(undefined)

根因:文档内嵌的修改记录中存在作者名为 undefined 的条目,getInitials 函数未做 null 判断。

修复

  • patch 三个编辑器的 app.jsgetInitials: function(t) { if (!t) return ''; ... }
  • index.htmlvar require = { urlArgs: "v=20260323" } 破除浏览器强缓存,确保 patch 生效

第 4 轮:恢复完整工具栏(commits b4c267c7d c65ca6903 55c866f2c

问题:打开文档后工具栏大量按钮缺失,只剩「文件」「视图」两个菜单。

排查过程

  1. mode: 'view' → 直接隐藏全部工具栏,移除后工具栏出现但仍有缺失
  2. permissions: { review: false, comment: false, chat: false, protect: false } → 每个 false 都会隐藏对应工具栏 tab,逐一移除
  3. 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 字段时 featuresundefined,访问其 .spellcheck 崩溃。

修复

  • patch presentationeditor/app.js:加 null guard t.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,由组件统一展示错误