描述 在 Qt WebEngine (PySide6) 环境中,通过 document:open-buffer 消息传递 Base64 编码的 docx 文件数据时,OnlyOffice 编辑器显示错误: 错误码:-85 错误描述:在打开文件时发生了错误。 文件内容对应于以下格式之一:pdf/djvu/xps/oxps,但该文件的扩展名不一致:docx。 复现步骤 使用 PySide6 QWebEngineView 加载 document 项目 从 Python 端通过 QWebEngineView.page().runJavaScript() 发送 document:open-buffer 消息 Python 端将 docx 文件读取为 bytes,转换为 Base64 后传递 编辑器加载后触发 onError 事件,错误码 -85 日志 [Embed API] Base64 first 50 chars: UEsDBAoAAAAAAIdO4kA... [Embed API] Decoded file header (hex): 50 4b 03 04 [Embed API] ✅ Valid DOCX file detected (PK header) [Embed API] Decoded Base64 to ArrayBuffer, size: 10216 [converter] Final documentData.bin header (hex): 50 4b 03 04 [converter] ✅ binData is valid DOCX [OnlyOffice] Creating new editor instance for: New_Document.docx type: docx [OnlyOffice] onAppReady triggered [OnlyOffice] binData type in onAppReady: object [OnlyOffice] onError: [object Object] [OnlyOffice] onError details: {"target":{"frameOrigin":"http://127.0.0.1:5000"},"data":{"errorCode":-85,"errorDescription":"在打开文件时发生了错误。<br>文件内容对应于以下格式之一:pdf/djvu/xps/oxps,但该文件的扩展名不一致:docx。"}} 关键观察: embed-api.ts 正确解码 Base64 为 ArrayBuffer(文件头 50 4b 03 04) converter.ts 正确传递 binData onlyoffice-editor.ts 中 binData type in onAppReady: object —— binData 在 onAppReady 中变成了普通对象,而不是 ArrayBuffer 环境信息 项目 版本 OnlyOffice API 7.4.1 Qt WebEngine PySide6 (Qt 6.x) Python 3.12+ Node.js 20.19.0+ pnpm 11.4.0 相关文件 embed-api.ts (makeArrayBufferFromPayload): async function makeArrayBufferFromPayload(payload: Record<string, any>): Promise<ArrayBuffer> { if (payload.data && typeof payload.data === 'string') { const binaryString = atob(payload.data); const bytes = new Uint8Array(binaryString.length); for (let i = 0; i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes.buffer; // 返回 ArrayBuffer } } onlyoffice-editor.ts (createEditorInstance): export function createEditorInstance(config: { fileName: string; fileType: string; binData: any; // 期望 ArrayBuffer media?: any; readonly?: boolean; }): Promise<void> { // binData 在 onAppReady 中变成了 object,而不是 ArrayBuffer } 可能的根本原因 1、Qt WebEngine 的 postMessage 序列化机制:Qt WebEngine 在处理 postMessage 时,ArrayBuffer 类型可能被转换为普通对象 2、window.DocsAPI.DocEditor 的 onAppReady 回调中,binData 丢失了类型信息 3、Vite 编译优化导致代码行为改变 期待行为 document:open-buffer 应正确打开 docx 文件 binData 在 onAppReady 中应保持为 ArrayBuffer 类型
Activity
chaxus commentedon Aug 4, 2026
@zjhtsgr 看起来都分析到原因了,可以提个 pr 来处理吗
zjhtsgr commentedon Aug 4, 2026
感谢建议。
我尽管分析出原因,并且在本地反复修改过 onlyoffice‑editor.ts、embed‑api.ts、converter.ts 这些文件,但始终没能修复完成。
希望技术更擅长的朋友可以来修复这段代码。我会持续关注该 issue,如果其他人愿意提交 PR,我会非常感谢。
chaxus commentedon Aug 4, 2026
@zjhtsgr 谢谢你这么细致的排查和记录。照着你贴的日志和分析,找到了一个大概率的根因:
asc_openDocument把转换后的文档内容作为原始 ArrayBuffer 通过postMessage传给 OnlyOffice 内部的编辑器 iframe,这一步依赖宿主环境对二进制类型的结构化克隆支持——嵌入式 WebView(比如 Qt WebEngine)在这块历史上确实容易出问题。项目里"新建空白文档"那条路径一直传的是 Base64 字符串且工作正常,所以这次改成统一走 Base64,理论上能绕开这个坑;顺带也把document:open-buffer之前完全不支持 Base64 payload 的缺口补上了。PR 在这里:#115,细节和分析过程写在 PR 里链的那篇 exploration 文档里了。
不过我这边没有 Qt WebEngine 环境,没法端到端复现你遇到的场景,所以这个修复目前只过了单元测试,还没有真实验证过。方不方便麻烦你在你的 PySide6 环境里拉这个分支(
fix/issue-113-embed-buffer-base64)试一下,看看还会不会报 -85?如果还是不行,麻烦把新的日志贴一下,我们再接着往下查。先谢谢你了 🙏zjhtsgr commentedon Aug 6, 2026
Issue #113 验证报告(最终版)
一、问题
在 Qt WebEngine (PySide6) 环境下,通过 document:open-buffer 传递 Base64 编码的 docx 文件,OnlyOffice 报错 -85:
"文件内容对应于以下格式之一:pdf/djvu/xps/oxps,但该文件的扩展名不一致:docx。"
二、官方修复内容
官方针对 #113 的修复方案:
lib/embed-api.ts:makeFileFromPayload 新增 payload.base64 处理分支
lib/onlyoffice-editor.ts:onAppReady 中将 binData 统一转为 Base64 字符串再发送 asc_openDocument
类型定义:buf 类型从 ArrayBuffer 放宽为 ArrayBuffer | string
修复逻辑:Base64 字符串在 postMessage 中不会失真,因此将二进制数据转成 Base64 后再发送,绕开 ArrayBuffer 跨边界失真问题。
三、验证结果
验证环境: Python 3.12 + PySide6 6.7.0 + Qt WebEngine,测试文件 10216 bytes
关键日志(已在 api.js 中添加日志):
[JS Log] [API.JS] sendCommand (internal) called, command: asc_openDocument
[JS Log] [API.JS] sendCommand called, command: asc_openDocument
[JS Log] [API.JS] OnlyOffice error code: -85
关键发现:
sendCommand 被正确调用,asc_openDocument 命令已发送
onAppReady 被调用了 4 次(重复创建编辑器实例)
asc_openDocument 被发送了 4 次
最终 OnlyOffice 内部返回 -85 错误
数据在到达 api.js 的 sendCommand 之前全部正确,但 -85 错误来自 OnlyOffice 内部 iframe 的响应。
四、根本原因
window.editor.sendCommand 调用 OnlyOffice SDK 的 sendCommand,数据通过 postMessage 发送给内部 iframe,OnlyOffice 内部解析 buf 时将其错误识别为 PDF 格式。
问题发生在 OnlyOffice SDK 内部,不在 document 项目层面:
document 项目 → window.editor.sendCommand
→ api.js (sendCommand) → postMessage
→ OnlyOffice 内部 iframe → 解析 buf → 判断为 PDF → 报 -85
五、需要官方进一步排查
检查 api.js 中 i() 函数的 o.data?.event 条件分支:当 o.data 包含 buf 但不包含 event 时,o 不会被 JSON.stringify,走 postMessage 结构化克隆路径。但 buf 是 Base64 字符串,结构化克隆应该安全。
在 OnlyOffice 内部 iframe 的消息接收端添加日志:需要确认 buf 在接收端是否保持为正确的 Base64 字符串,还是被误解析为其他格式。
检查 Qt WebEngine 对 postMessage 的特殊处理:Qt WebEngine 可能对跨 iframe 的 postMessage 做了额外的序列化处理。
如果数据在 postMessage 过程中被损坏,需要在 OnlyOffice SDK 层面修复,而不是在 document 项目层面。
六、结论
官方修复未解决 #113。 数据在 sendCommand 发送前是正确的(Base64 长度 13624),但 OnlyOffice 内部解析时出错。问题根源在 OnlyOffice SDK 内部的 postMessage 接收和解析逻辑,需要 OnlyOffice 官方在 SDK 层面排查修复。
fix(embed): send Editor.bin verbatim as a latin1 string, not base64-w…
test(embed): add Qt WebEngine host demo and open-buffer e2e regressio…
chaxus commentedon Aug 10, 2026
@zjhtsgr 你的验证结论是对的——上一版修复确实无效,让你白测了一轮,抱歉。不过根因和你推断的不一样,不在 OnlyOffice SDK,在我这边的修复代码本身。这次已经定位清楚并且在真实 Qt WebEngine 环境里验证过了,展开说一下:
真正的根因
x2t 转换出来的 Editor.bin 本身就是一个文本容器,以
DOCY;v5;<长度>;<base64>开头;而 SDK 对字符串类型的 buf 只做字面签名检查(buf.slice(0,4) === 'DOCY'),不会先做 base64 解码。上一版修复把 bin 整体又btoa了一层,buf 变成了RE9DWT...开头,SDK 自然认不出来——所以它在所有环境下都是坏的:你的 Qt WebEngine 里表现为 -85 报错,桌面 Chrome 里表现为永远卡在 "Loading document",症状不同、根因相同。修复与验证
分支已更新(8a9114b):改为把 bin 原文按 latin1 逐字节转成字符串发送,保留
DOCY;签名头。字符串跨 postMessage 依然不会失真,所以你最初报告的 ArrayBuffer 跨 WebView 边界失真问题同样被覆盖。这次我没有只跑单测——用 PySide6 6.11.1 + Qt WebEngine 在本地复刻了你的宿主场景做了双向验证:
asc_openDocument的 buf 前缀为DOCY;v5;,onDocumentReady正常触发,文档打开成功;RE9DWT,触发 onError(就是你看到的 -85),文档打不开。复现用的宿主代码也一并提交进了仓库:
demo/qt-webengine/(README 里有运行方法),你可以直接拿它和你自己的宿主代码对照。另外 CI 里也加了端到端回归测试(test/e2e/embed-open-buffer.spec.ts),以后这条链路再出问题会直接红。麻烦你最后确认一轮
重新拉
fix/issue-113-embed-buffer-base64分支构建后测试。如果还有问题,在 sendCommand 处打印buf.slice(0, 8)贴上来即可一击定位:DOCY;v5;RE9DWTUEsDB最后一个建议:你日志里 onAppReady 被调用了 4 次,说明宿主侧把 open 消息发了 4 次(每次都会销毁重建编辑器)。这与 -85 无关,但建议查一下 PySide6 那边是否有重试逻辑,只发一次就够了。
fix(embed): send document buffer as base64 to avoid ArrayBuffer loss …
fix(embed): send Editor.bin verbatim as a latin1 string, not base64-w…
test(embed): add Qt WebEngine host demo and open-buffer e2e regressio…
chaxus commentedon Aug 15, 2026
已在 v9 版本修复:编辑器底座升级为 OnlyOffice 9.3(旧版对 document:open-buffer 的二进制处理会破坏文件头,导致引擎误判为 pdf/djvu/xps 报 -85)。现在 open-buffer 直接以原始字节走 blob URL 打开,并有真实编辑器 E2E 回归守护(多 sheet 工作簿 open-buffer 打开 + 保存往返)。本次 v9 版本合入 main 部署 edit.chaxus.com 后生效;如更新后仍能复现,请重新打开此 issue。
feat(v9): open PDFs via the vendor pdf editor, surface editor errors,…
test: add regression coverage for GitHub-issue-relevant app logic
fix(embed): send document buffer as base64 to avoid ArrayBuffer loss …
fix(editor): the x2t proxy must not fetch a document as if it were a URL