1. 三个名字背后,其实是三种完全不同的“在线编辑”方案
先说点实际的。很多人第一次看到“unver、jsspredsheet、onlyoffice”这几个词放在一起,会以为它们是同类产品对比。其实不是,Univer(没错,你搜到的 unver 就是它,只是经常被缩写或者拼错)、Jspreadsheet(同样,jsspredsheet 是它的常见笔误)和 OnlyOffice,虽然都做在线编辑,但做的事情完全不同。
Univer 是一个开源的表格式办公套件,定位有点像 Google Sheets 的本地化替代,但它不是让你访问一个网站来用,而是给你一套组件,你可以把它嵌到自己的系统里,实现单元格编辑、公式计算、条件格式、筛选、图表甚至协同编辑,整个渲染是基于 Canvas 的,所以用起来比传统 DOM 表格流畅很多。
Jspreadsheet 则是一个更轻量的 JavaScript 数据表格插件,依赖 jQuery,主打的是快速生成可编辑的数据网格。它更适合做管理后台里的那种“在线录入表格”,而不是一个完整的 Office 套件。它的体积小、上手快,但功能深度远不如 Univer 或 OnlyOffice。
OnlyOffice 是最重的一个,它是一整套办公套件,包含文本编辑器、电子表格、演示文稿,并且带服务端。你部署了 OnlyOffice Document Server 之后,通过 API 就能在自己的业务系统里打开 docx、xlsx、pptx,实现多人实时协同编辑。它的核心价值在于“兼容 Office 格式”,而 Univer 和 Jspreadsheet 更偏“前端交互”。
所以你首先要搞清楚自己要解决什么问题:是想在自己的网页里做一张可编辑的表格?还是想做一个正经的在线文档协同系统?这两种需求的选型方向完全不同。
我最初接触这三样东西,是因为要给客户的内部系统加一个在线预览和编辑 xlsx 的功能。当时我先试了 Jspreadsheet,发现它处理大表格性能不错,但一碰到 Excel 自带的复杂样式、公式跨表引用就力不从心。然后试了 Univer,它在表格内核上做得确实更接近 Excel,但依然没法原生打开 xlsx 文件并保留所有元素。最后才回过头认真研究 OnlyOffice 的文档服务。
这一篇就把我自己的初步实践过程写清楚,尤其是 OnlyOffice 的 Docker 镜像安装、协同编辑接入,以及很多人搜过的那个有点奇怪的参数assemblyFormatAsOrigin=true到底是怎么回事。
2. 搭建 OnlyOffice 文档服务器:Docker 镜像安装的前前后后
2.1 为什么第一步就走 Docker 镜像安装
OnlyOffice 有两条部署路线:一条是装全家桶(Community Server + Document Server),一条是只装 Document Server。全家桶带项目管理、CRM、邮件这些协作功能,看起来功能很全,但对于只想在自己系统里嵌入在线编辑的开发者来说,太重了。我实际测试下来,只部署 Document Server 就够了,它专门负责文档的保存、转换、协同编辑,对外提供 API。
而 Document Server 最省事的安装方式就是 Docker 镜像。你不需要在一台空机器上手动装 PostgreSQL、Redis、RabbitMQ、Nginx 这些依赖,镜像里全都有。你要做的只是把容器跑起来,把数据卷挂出来,把端口映射好。
如果你是从零开始且没有太多服务器运维经验,我强烈建议直接用 Docker。OnlyOffice 官方提供的文档里写的安装步骤也是默认以 Docker 为主的,社区里遇到安装问题,十有八九也是围绕 Docker 环境讨论。
2.2 最简部署命令和参数说明
先看一下我最开始用的最简启动命令:
sudo docker run -i -t -d -p 80:80 --restart=always \ -v /app/onlyoffice/DocumentServer/logs:/var/log/onlyoffice \ -v /app/onlyoffice/DocumentServer/data:/var/www/onlyoffice/Data \ -v /app/onlyoffice/DocumentServer/lib:/var/lib/onlyoffice \ -v /app/onlyoffice/DocumentServer/db:/var/lib/postgresql \ onlyoffice/documentserver:latest解释一下几个关键点:
-p 80:80是把容器里的 80 端口映射到宿主机 80 端口。如果你的 80 端口已经被 Nginx 占用,可以改成-p 8080:80,后面访问地址就带端口。--restart=always保证服务器重启后容器自动拉起,这个很重要,不然后面隔三差五发现服务挂了。- 数据卷必须挂。
/var/log/onlyoffice是日志目录,排查安装问题全靠它;/var/www/onlyoffice/Data是配置数据;/var/lib/postgresql是数据库目录,不挂的话容器一删,所有文档元数据都没了。
启动之后,在浏览器访问http://你的服务器IP,如果能看到一个“ONLYOFFICE Document Server”的欢迎页,说明基本装好了。接下来就可以用/web-apps/apps/api/documents/api.js这个入口来对接前端的 JS API 了。
我后来在生产环境里实际用的命令比这个多加了几个参数,比如时区、JWT 密钥、内存限制。这里有一个容易忽略的点:OnlyOffice Document Server 对系统内存有要求,官方建议至少 2GB,如果你在低配机器上强行跑,文档转换时很容易崩。查看容器日志时如果看到内存相关的报错,优先把内存升上去,不要先怀疑代码。镜像安装的优点是可以省去手动初始化数据库的步骤,缺点恰恰是出了问题不容易看到底层,所以日志目录一定要提前规划好,后面排查安装问题全靠它。
2.3 常见安装问题:端口冲突、域名校验、文档转换失败
我在搭建过程中和后来帮朋友排查时,最常见的安装问题基本集中在下面几类,我用表格列一下:
| 现象 | 可能原因 | 处理办法 |
|---|---|---|
| 容器启动了但页面打不开 | 宿主机 80 端口被其他服务占用 | 换一个宿主端口映射,比如-p 8081:80,并确认防火墙放行 |
| 打开欢迎页正常,但预览文档一直转圈 | 服务器无法访问自身生成的下载链接 | 检查回调 URL 配置,确保前端传的document.url能被 Document Server 访问到,这个“自我访问”问题在内网部署时极其常见 |
| 上传 docx 后显示“未找到文件” | 容器内部 DNS 无法解析外部下载地址 | 给容器配置 DNS 或直接用外网可访问的 URL,不能填localhost |
| 能打开文档但保存失败 | JWT 密钥不一致 | 前端初始化DocEditor时配置的token和 Document Server 环境变量里的JWT_SECRET必须一致 |
| 转换耗时长或失败 | 服务器内存不足,或目标格式不受支持 | 提高内存、排查日志确认格式是否在支持列表内 |
这里面我特别想强调“文档转换失败”这一条。OnlyOffice 在前端展示 docx、xlsx 时,并不是直接把原始文件拿来显示,而是会先转换排版和内部格式。如果 Document Server 所在服务器访问不到文件 URL,转换必然失败。很多人在本地联调时用http://localhost传 URL,Document Server 在容器里,当然访问不到宿主机的 localhost。正确做法是在同一局域网内直接用宿主机 IP,或者把文件转成 base64 走file字段。
还有一类问题:修改了 JWT_SECRET 之后,旧的容器还是用自己的密钥,导致前端一直报鉴权失败。这时候不要只改配置,要重启容器并清一下 Redis 缓存。OnlyOffice 的 JWT 机制排查起来很绕,建议一开始就统一用一个固定的强密钥,前后端、环境变量保持一致,别频繁换。
3. 在线协同编辑接入:从 API 初始化到assemblyFormatAsOrigin=true
3.1 前端接入的完整套路
OnlyOffice 在线协同编辑不是一个纯前端组件,它需要你的业务系统把文档信息告诉 Document Server,由 Document Server 负责加载文档、分发给各个在线用户。前端要做的事情其实很集中:
- 在页面引入
https://你的服务器地址/web-apps/apps/api/documents/api.js。 - 构造一个配置对象,包含文档类型、文档 URL、权限、编辑器模式、回调地址等。
- 初始化
new DocsAPI.DocEditor("placeholder", config)。
一个最小可用的配置大致长这样:
const docEditor = new DocsAPI.DocEditor("docs-container", { document: { fileType: "xlsx", key: "unique-document-key-001", title: "销售统计表.xlsx", url: "http://宿主机IP/example.xlsx", }, documentType: "cell", editorConfig: { mode: "edit", callbackUrl: "http://宿主机IP/callback", user: { id: "user-001", name: "张三", }, }, width: "100%", height: "100%", });有几个细节值得注意:
document.key是一个业务系统自己维护的文档唯一标识。OnlyOffice 用它做缓存判断,如果同一个 key 的文档内容变了,你需要更新 key,否则服务端可能不会重新加载新内容。很多人改了文件但界面上还是旧的,多半是 key 没有更新。document.url必须是 Document Server 能访问到的地址。如果你的文件在业务系统内网,Document Server 也在同一内网,直接填内网 IP 就行;如果用公网服务器,就要填能被公网访问的 URL。editorConfig.user是当前编辑人的身份。协同编辑时,多人同时打开同一文档,就是靠这个 user 区分不同用户的头像和光标。callbackUrl是 Document Server 在保存文档时通知你业务系统的地址。如果你想把编辑后的文档存回自己的服务器,必须在回调接口里接收数据并做处理,否则文档只保存在 OnlyOffice 的临时目录里。
3.2assemblyFormatAsOrigin=true到底是干嘛的?
这个参数在 OnlyOffice 的讨论区里出现频率很高,尤其是一些搜索热词里直接和 onlyoffice 放在一起。它最初出现在 Document Server 的内部转换请求接口里,作用简单说就是:让文档在转换和渲染时,尽量以原始格式的规则来处理,而不是强制转换成 OnlyOffice 内部格式后丢失一部分样式信息。
我自己在实现“保留 Excel 原始表格样式”时踩过这个坑。默认情况下,OnlyOffice 对 xlsx 的解析会做一层内部归一化,如果源文件某些样式比较冷门,比如自定义数字格式、特殊条件格式规则,转换后可能会出现略微差异。而显式带上assemblyFormatAsOrigin: true,就是告诉服务端:这个文档在排版解析时,优先尊重源文件的格式定义。
这里要说明一点,它不是前端 API 里的常规配置项,更多是出现在 Document Server 的转换配置、自定义插件配置或者某些集成方案中。很多人会把它写进前端的document配置里,结果是无效的。正确的使用方式要看具体集成场景,如果用的是只读预览方案(比如把 Office 文件转成 PDF 预览),可以在转换请求里带上这个参数;如果直接走DocEditor编辑器,这个参数一般不需要手动控制。
我给的实操建议是:
- 在做转换接口调用时,手动加上
assemblyFormatAsOrigin: true,能明显减少“转出来的 PDF 和原文件排版差好多”的抱怨。 - 如果发现加了参数后某些复杂文档依然异常,优先排查源文件格式是否官方支持,而不是死磕参数。
- 不要每一处都无脑加。遇到有密码保护的文档、带有宏的文件,转换逻辑可能会不同,这时候先去掉这个参数,对比两个结果再判断。
3.3 协同编辑验证:至少要测这三种情况
Once Document Server 跑起来、前端接入完成,就要验证协同编辑是不是真的可用。很多人以为两个人同时打开一个文档就算协同,其实 OnlyOffice 的协同编辑有严格的前提条件:同一个文档 key、同一个 Document Server 实例、网络互通。
我应该验证的最小场景有三个:
场景一:两人同时编辑同一单元格。打开同一个文档后,A 用户在 B1 单元格输入内容,B 用户的界面应该能看到光标位置和实时输入状态。如果 B 看不到任何 A 的操作,最可能的问题是两个人加载文档时用了不同的key,或者是通过不同 URL 打开导致服务端认为是两个不同文档。
场景二:增量保存与回调。A 编辑后关闭文档,业务系统应该收到callbackUrl的 POST 请求,里面带文档状态和临时文件名。这一步很多人会忽略,结果就是“编辑完了以为保存了,实际上 OnlyOffice 只是把内容存到了自己的临时目录”。回调接口至少要返回{"error": 0}的 JSON,OnlyOffice 才会认为保存成功。
场景三:断线重连。把 Document Server 容器重启,正在编辑的页面应该能自动重连。如果一直转圈,要去看容器日志里是否有 WebSocket 相关的报错,尤其是在用了 Nginx 反向代理的情况下,WebSocket 的Upgrade头必须正确转发。
我这部分就踩过一次:前端配置了协同编辑的权限选项,但 Document Server 的 JWT 校验未打开,于是任何人都能通过伪造参数来改文档。这个问题在开发环境不容易暴露,但上线前务必确认 JWT 已开启,否则你的文档等于裸奔。开 JWT 的方法是在启动容器时设置环境变量,同时在请求的配置里附带生成好的 token。
4. 轻量嵌入式方案实测:Univer 和 Jspreadsheet 能替你省哪些事
4.1 三者的能力边界对比
在深入用 OnlyOffice 之后,我反而对 Univer 和 Jspreadsheet 的定位更清楚了。它们之间的关系不是替代,而是互补。用一个表格来对比会更直观:
| 对比项 | Univer | Jspreadsheet | OnlyOffice Document Server |
|---|---|---|---|
| 部署形态 | 前端组件,可本地打包部署 | 前端组件,可本地打包部署 | 独立服务端,需要 Docker 或物理机部署 |
| 核心优势 | 类 Excel 体验,公式引擎强,Canvas 渲染 | 轻量,上手快,适合数据录入场景 | 格式兼容度高,原生编辑 docx/xlsx/pptx |
| 协同编辑 | 支持,但需要配合服务端 | 默认不支持实时协同 | 原生支持实时协同 |
| 打开 Excel 文件 | 部分兼容,复杂样式可能丢 | 不擅长复杂 Excel 样式 | 官方主打,格式还原度高 |
| 适合场景 | 需要在线表格且想控制前端体验的产品 | 后台系统的数据表格录入 | 企业办公文档在线预览与协作 |
| 开发成本 | 中等,需要理解 Univer 的渲染机制 | 低,套表格组件即可 | 较高,要部署和维护 Document Server |
这个表格不是我随便画的,都是实际跑过之后总结的。
4.2 Univer 的实际使用体验
Univer 给我的第一印象是“长得像 Excel,速度很快”。它用 Canvas 画表格,滚动和大数据量渲染确实比传统 DOM 表格强。如果你要做一个类似在线报表编辑的工具,它的公式、筛选、条件格式、图表这些模块都做得比较完整,而且完全开源,不受制于商业授权。
但它也有明显短板。我拿一个带透视表、条件格式、跨 sheet 引用的 xlsx 文件测试,Univer 加载后部分条件格式失效,公式结果倒是能正常算。这说明它的 xlsx 解析协议还达不到 100% 兼容 Office 的程度。如果你的目标是“用户上传一个 Excel,在上面继续编辑,保存后格式不能差太多”,Univer 目前还不够稳。
比较适合 Univer 的场景是:你的系统本身就需要一个“动态表格”,比如在线问卷、数据填报、项目管理表,数据是业务系统生成的,而不是用户上传的真实 Excel。这种场景下用 Univer 做前端表格,等于给产品加了一个非常强的交互层。
4.3 Jspreadsheet 的适用边界
Jspreadsheet 更轻,依赖jquery,官方文档很简洁。它在表格编辑、行列增删、数据验证方面做得不错,适合做后台管理系统里的数据录入组件。我见过很多后台项目用它替代原生的<table>来录入批量数据,配合下拉框、日期选择器,开发效率很高。
它最大的问题是:功能性太偏“网格”,而不是“电子表格”。比如跨 sheet 引用、数组公式、图表联动这些在 Excel 里很基础的能力,Jspreadsheet 基本没有。如果你的需求只是“填一张表然后提交到后端”,选它没错;如果你要做复杂财务分析,它撑不住。
有人可能想用 Jspreadsheet 的“导出 Excel”功能,实际导出的文件也就是 CSV 级别的效果,样式基本没有。所以这类轻量方案更适合内部数据管理,不适合面向最终用户的正式文档导出。
4.4 能不能把三者组合起来用?
我的答案是:能,但要注意各干各的活。
我在一个实际项目里做过这样的组合:用户在前端用 Jspreadsheet 快速录入数据,点保存后把数据传给后端生成 xlsx 模板文件;后端拿到 xlsx 之后再用 OnlyOffice Document Server 渲染成预览界面,用户点“在线编辑”就进入 OnlyOffice 编辑模式,做精细调整和多人协作。这样既保证了录入效率,又拿到了 Office 级别的格式兼容,关键是每个工具都只用自己最擅长的部分。
如果你想更进一步,可以把 Univer 对接到 OnlyOffice 的文档存储层,用 Univer 做轻量展示,OnlyOffice 做完整编辑。不过这一步的维护成本不低,除非业务确实需要,否则不建议一上来就这么做。
5. 从初步尝试到稳定使用的几个教训
写到最后,分享几个我自己常用的排查思路,比任何配置模板都更耐用。
第一个教训:不要急着上全家桶。Once 我图省事直接把 OnlyOffice Community Server 也装上了,结果它的协作模块依赖邮件服务器、外部存储配置,维护成本一下子涨了很多。后来我全部退回到只部署 Document Server,业务系统自己控制文档的存储和权限,反而简单可靠。OnlyOffice Document Server 本身是一套很完整的文档服务,你不要被“全家桶”迷惑,能用 API 解决的集成就别碰全家桶。
第二个教训:日志是最好的老师。不管是 Docker 镜像安装问题还是在线协同编辑异常,第一步永远是看容器日志。docker logs会比前端控制台多暴露很多底层信息。有一次我配置 JWT 后始终报 401,前端看不出任何问题,只有日志里提示Invalid token,顺着这个信息才知道是环境变量没生效,重启容器后解决。所以排查时别靠猜,先看 logs。
第三个教训:文档缓存是协同编辑里最容易被忽略的坑。Document Server 会根据key缓存文档,某个 key 对应的文档内容已经更新,但前端没换 key,服务端就会一直返回旧版本。这在前端联调时很难发现,因为刷新页面也可能拿到缓存。一个稳妥的做法是在文档保存成功后,强制生成一个全新的 key,而不是随便传一个固定的字符串。这也能避免多个用户操作同名不同内容文件时串数据。
最后一点:assemblyFormatAsOrigin 这类参数,别只当黑盒抄。它的名字已经说明了作用——按原始格式组装,本质是让服务端转换时尽量贴着源文件的结构走。理解了这个含义,你再遇到 OnlyOffice 打开文档时样式偏移的问题,就清楚该往哪个方向排查了。这和缩略词、热搜词没关系,纯粹是你看懂参数逻辑之后自然具备的判断力。
这三个项目我现在都在用:Univer 负责对外展示的炫酷表格界面,Jspreadsheet 负责内部快速数据录入,OnlyOffice Document Server 负责正式的文档协同。它们各管一段,配合下来效果比硬用某一个强得多。如果你的需求就是给网站加个表格,没必要上来就部署 OnlyOffice;如果你想做真正意义的多人编辑企业文档,那 Jspreadsheet 和 Univer 再怎么折腾也代替不了 OnlyOffice 的位置。