
最近接了个内部系统的需求要在现有 Spring Boot 管理系统里直接打开 Word/Excel/PPT 并在线编辑。一开始想着装个转换工具把文档转成 PDF 做在线预览就完事但业务方明确要求能改、能保存、最好还能多人协同这就必须上真正的在线编辑服务。调研了一圈OnlyOffice Document Server 是当前开源方案里集成成本最低、兼容 Office 格式最稳、社区最活跃的选择。这篇文章围绕Spring Boot 集成 OnlyOffice这件事把部署和开发两条线拆开讲透包括架构怎么设计、容器怎么起、参数怎么配、代码怎么写、JWT 怎么签、回调怎么收、以及我实际踩过的坑。适合两类人看一是想在现有 Java 管理系统里加在线 Office 编辑能力、但没接触过 OnlyOffice 的后端开发二是在做技术选型、想快速评估集成工作量的架构同学。默认你会 Spring Boot 和 Docker 的基本用法但不假设你之前碰过 OnlyOffice。整个事情拆开就两件事把 OnlyOffice 文档服务器跑起来让 Spring Boot 应用和它正确对话。前者一条命令的功夫后者才是真正花时间的地方。1. 集成前的整体设计与架构梳理1.1 OnlyOffice 到底解决什么问题OnlyOffice Document Server 是一个开源的在线 Office 套件服务核心能力是在浏览器里直接渲染和编辑 Word、Excel、PPT、PDF 等格式且编辑体验高度贴近 Office 原生界面。它能做的不只是看而是真的改改完能保存回你的存储系统还支持多人同时开同一份文档实时协同编辑、评论、版本历史。这意味着我不用在服务器上装 WPS 或 Microsoft Office不用自己写格式解析不用操心前端怎么渲染 docx 里的复杂排版图、表格、公式。前端我只负责给一个 div 加一段配置编辑器 UI 和所有交互逻辑全由文档服务器输出Spring Boot 这边只需要管好三件事提供文件的下载地址、接收保存结果的回调、维护业务侧的元数据。它和传统方案的本质区别在于其他方案多半是转 PDF 只读预览或者自己实现简单富文本而 OnlyOffice 是完整的桌面级编辑器体验。如果你的业务场景是合同在线签署、公文协同编辑、教学课件管理、ERP 附件在线修改它能直接满足需求。1.2 技术选型Docker 部署为什么是首选OnlyOffice 官方提供三种部署方式Debian/Ubuntu 安装包、集成版套件包、Docker 镜像。我个人的建议非常明确生产环境优先 Docker除非公司强制要求裸机部署。原因很简单。OnlyOffice Document Server 内部依赖一整套组件Nginx 负责路由和静态资源RabbitMQ 做协作消息队列Redis 做缓存PostgreSQL 存元数据和历史版本还包含字体库、转换服务。如果用安装包部署第一步装依赖、配置数据库、改 Nginx 配置就够折腾一整天升级的时候更是噩梦。而官方 Docker 镜像把这些全部打包好了起一个容器就是一套完整环境升级替换镜像即可。代价是容器内的细节被封装了出了问题排查要靠日志目录和官方文档。但只要遵守数据目录挂载出来、日志挂载出来、端口固定这三个原则运维成本反而比裸机更低。1.3 三端调用链路理解了整个调用链后面所有配置和代码就都顺了。这个系统里有三个角色浏览器端用户打开我们的管理系统页面页面里嵌着 OnlyOffice 编辑器 iframe。Spring Boot 后端管理系统的业务服务负责登录鉴权、文件的存储与读取、生成编辑器初始化配置、接收保存回调。OnlyOffice 文档服务器独立的部署服务负责渲染编辑器界面、维护编辑状态、把编辑结果回传给后端。一次完整的编辑流程是这样的用户在管理系统里点编辑按钮前端请求 Spring Boot 的配置接口。Spring Boot 从存储里读出文件生成一个包含文件下载地址、回调地址、用户信息等内容的配置 JSON并用 JWT 签名后返回。前端用这段配置初始化 DocsAPIOnlyOffice 编辑器 iframe 加载出来。编辑器内部会主动向 Spring Boot 的下载接口请求文件内容收到后渲染成可编辑的界面。用户编辑过程中OnlyOffice 和它的协作服务通信点击保存或编辑器关闭时OnlyOffice 会把编辑后的文件通过回调接口推回给 Spring Boot。Spring Boot 收到回调把新文件保存到存储本地磁盘、OSS、数据库都行更新业务记录。这个链路里最容易出问题的点是谁能访问谁浏览器要能访问 Spring Boot 和 OnlyOfficeOnlyOffice 容器内部也要能访问 Spring Boot 的下载和回调地址但 Spring Boot 不一定需要访问 OnlyOffice 容器编辑配置、回调都由 OnlyOffice 主动发起。搞混这个访问关系后面所有打不开转圈保存失败的排查都会绕弯路。2. 文档服务器部署一条 Docker 命令起步2.1 环境要求与镜像选择先说说硬件底线。OnlyOffice 官方给的建议是 2 核 4G 起步实际用下来个人开发或者并发低于 10 人的内部系统2 核 4G 勉强能跑但首次打开大文档时转换服务会比较慢并发 20 人以上建议 4 核 8G。磁盘方面除了系统盘最好单独给文档服务器预留存储空间因为编辑历史、缓存文件都会落在容器数据目录里。镜像直接用官方仓库的onlyoffice/documentserver标签建议不要裸用latest每次拉取结果不可控生产环境一旦行为变化很难追查。我是固定使用7.3.3这样的具体版本号升级走测试环境先行验证的流程。还有一点要提前确认服务器内网 IP 之间网络是否互通。文档服务器如果和 Spring Boot 部署在不同机器上要保证 80/8088 端口和 Spring Boot 的业务端口互相能访问云服务器还要检查安全组策略。这看起来是废话但我见过太多项目对接不上最后发现是安全组没放行。2.2 Docker 运行命令与参数解读在部署目录下提前建好宿主机挂载目录用下面这条命令启动mkdir -p /data/onlyoffice/logs /data/onlyoffice/data /data/onlyoffice/lib /data/onlyoffice/db sudo docker run -i -t -d \ -p 8088:80 \ --restartalways \ -e JWT_ENABLEDtrue \ -e JWT_SECRETMySecretKeyForOnlyOffice2024!# \ -v /data/onlyoffice/logs:/var/log/onlyoffice \ -v /data/onlyoffice/data:/var/www/onlyoffice/Data \ -v /data/onlyoffice/lib:/var/lib/onlyoffice \ -v /data/onlyoffice/db:/var/lib/postgresql \ --name onlyoffice-documentserver \ onlyoffice/documentserver:7.3.3逐个说参数的意义以后调优不会抓瞎-p 8088:80容器内部 Nginx 固定监听 80宿主机对外开放 8088。端口可以换但注意如果映射成非 80 端口前端引用的 api.js 路径、回调地址都要跟着用http://服务器IP:8088完整域名否则会出现资源加载 404。--restartalways服务器重启后容器自动拉起。OnlyOffice 里面的 Postgres、RabbitMQ 是一起起的这个参数能避免人工恢复的麻烦。JWT_ENABLEDtrue开启 JWT 鉴权后面章节细说现在先记住必须开。JWT_SECRETJWT 密钥。这个值要记好Spring Boot 端要用同一个字符串生成 token两边不一致所有请求都会被拒。长度建议至少 32 个字符且包含大小写字母数字与符号别用123456这种。四个-v挂载日志logs、转换缓存和证书data、程序库lib、数据库文件db。挂载的意义在于容器删了重建文档历史、用户数据、证书都还在。不挂载的后果就是容器一删全部归零调试的时候反复重建容器就会莫名其妙丢数据。启动后等 30 秒左右容器内部服务初始化完成后验证curl http://localhost:8088/healthcheck返回true说明核心服务正常。然后打开浏览器访问http://localhost:8088/welcome/能看到欢迎页和示例编辑入口就说明部署基本成功。2.3 几个容易忽略的配置项如果只有本地开发上一节的命令已经够了。但生产环境我建议再加三个姿势一是时区问题。容器默认 UTC 时间和国内服务器差了 8 小时会导致文档服务器内部的任务调度、日志时间戳和业务对不上。启动时加上-e TZAsia/Shanghai二是 HTTPS。文档建议生产环境用 HTTPS 访问文档服务器否则浏览器会阻止某些功能尤其是浏览器较新版本对非安全上下文的限制。大多数团队的实际情况是文档服务器不直接暴露公网而是由 Nginx 反向代理统一收口 SSL。这个方案完全可行反向代理层把 SSL 终止掉转发到容器的 8088 端口即可。要注意 Nginx 转发时把Host、X-Forwarded-*头都带上OnlyOffice 会基于这些头生成内部资源地址。三是调整上传大小限制。原始容器内置的 Nginx 对上传请求有限制默认较大文档上传时会直接断掉。如果需要支持超大 PPT/视频嵌入类的文档需要进容器修改配置或挂载自定义配置。对绝大多数业务来说默认值够用真遇到再说这里提一下让你知道有这个东西。3. JWT 安全机制与前后端衔接3.1 为什么必须开启 JWT只部署文档服务器、不接后端在欢迎页手动传文件编辑是完全能用的。但一旦接入业务系统必须面对一个安全问题文档服务器对外暴露了下载文件的能力如果任何请求都能构造一个 URL 让它下载那文件内容就等于裸奔了。OnlyOffice 的 JWT 机制解决的就是这个信任问题。它规定所有发给文档服务器的关键 API 请求都必须在携带一个由约定密钥签名的 JSON Web Token。因为只有业务后端知道这个密钥所以能签发合法 token 的只有后端自己。文档服务器收到请求后用同一个密钥验签通过才处理。这样第三方即使拿到了下载链接没有 token 也触发不了工作流。反过来OnlyOffice 回调 Spring Boot 时也会带 tokenSpring Boot 验签通过才信任回调来源。两边互验这就是一套完整的双向信任体系。3.2 密钥同步与 Token 生成密钥在部署时通过JWT_SECRET环境变量写进了文档服务器Spring Boot 这边必须用一模一样的字符串。我习惯把它放到配置中心或环境变量里避免硬编码在代码中。后端生成 token 用的库是jjwtmaven 依赖如下dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-jackson/artifactId version0.11.5/version scoperuntime/scope /dependency工具类写法如下核心就是创建 HS256 签名 tokenimport io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import javax.crypto.SecretKey; import java.util.Date; import java.util.Map; public class OnlyOfficeJwtUtil { private static final String SECRET MySecretKeyForOnlyOffice2024!#; public static String createToken(MapString, Object claims) { SecretKey key Keys.hmacShaKeyFor(SECRET.getBytes()); return Jwts.builder() .setClaims(claims) .setIssuedAt(new Date()) .signWith(key, SignatureAlgorithm.HS256) .compact(); } public static Claims parseToken(String token) { SecretKey key Keys.hmacShaKeyFor(SECRET.getBytes()); return Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token) .getBody(); } }注意Keys.hmacShaKeyFor对密钥长度有要求HS256 算法需要至少 32 字节所以密钥别设太短否则运行时会直接报错。另外配置接口返回给前端的配置 JSON整体会作为 claims 签入 token文档服务器收到后会把 token 解出来并合并到配置中这样前端拿到的配置是明文配置 token双份核心安全逻辑都在 token 里。3.3 前端加载编辑器的最小集成前端集成比想象中简单。只需要在页面引入文档服务器的 api.js 脚本然后创建一个 div 容器最后实例化DocsAPI.DocEditor并传入配置。先不要急着做花哨的封装建议先弄一个静态测试页面把整个链路打通了再搬进项目。最小可用的静态 HTML 长这样!DOCTYPE html html langzh-CN head meta charsetUTF-8 titleOnlyOffice 编辑测试/title script typetext/javascript srchttp://服务器IP:8088/web-apps/apps/api/documents/api.js/script /head body div idofficeEditor stylewidth:100%;height:720px;/div script var config { document: { fileType: docx, key: test-demo-key-001, title: 测试文档.docx, url: http://服务器IP:8080/api/editor/download/1, permissions: { edit: true, download: true } }, documentType: word, editorConfig: { callbackUrl: http://服务器IP:8080/api/editor/callback/1, mode: edit, lang: zh-CN, user: { id: admin, name: 系统管理员 } }, width: 100%, height: 100%, type: desktop }; var docEditor new DocsAPI.DocEditor(officeEditor, config); /script /body /html这里的key是文档在文档服务器侧的标识。同一个逻辑文档只要 key 不变编辑历史会延续key 变了文档服务器把它当成新文档处理历史归零。所以生成 key 的规则非常重要我常用的方案是文件的哈希值 版本号或者文件ID 最后修改时间保证内容变了 key 就变内容没变 key 不变。用固定 ID 会导致永远没有新版本历史用随机 UUID 会导致每次打开历史都断掉这两个都是常见错误。配置里的url必须保证文档服务器容器内部也能访问也就是说不能写localhost或127.0.0.1必须写服务器在局域网内的 IP 或者域名。同理callbackUrl也必须让文档服务器容器内部能访问到 Spring Boot。4. Spring Boot 后端开发实战4.1 业务需求与接口清单后端要实现的接口其实就四个配置接口、下载接口、回调接口、以及一个可选的文件列表接口方便前端选择哪个文件打开。我的工程结构在常规 controller/service/mapper 基础上把 OnlyOffice 相关的单独放在controller/OfficeEditorController.java里和普通文件接口隔离方便以后想要升级或者替换文档服务时快速定位改动范围。文件存储我建议第一版先做简单一点存本地磁盘。数据库里只存文件路径、文件名、文件类型、大小、版本号、内容哈希这些元数据。等业务跑通了把存储层换成 OSS 或分布式文件系统接口签名不变影响面很小。一上来就搞分布式存储很容易被存储问题干扰主线。4.2 配置接口实现配置接口的核心职责是根据用户请求的文件 ID组装出前一篇提到的编辑器 config JSON整体签名后返回。RestController RequestMapping(/api/editor) public class OfficeEditorController { Value(${onlyoffice.doc-server-url}) private String docServerUrl; Value(${onlyoffice.jwt-secret}) private String jwtSecret; Autowired private FileInfoService fileInfoService; Autowired private OnlyOfficeService onlyOfficeService; GetMapping(/config/{fileId}) public MapString, Object getEditorConfig(PathVariable Long fileId, HttpServletRequest request) { // 1. 从库里查出文件信息 FileInfo file fileInfoService.getById(fileId); if (file null) { throw new BusinessException(文件不存在); } // 2. 计算文档 key规则文件内容哈希 版本号 String documentKey file.getContentHash() _ file.getVersion(); // 3. 构建请求域名注意要用外网/内网可达的地址不能写 localhost String serverBaseUrl buildBaseUrl(request); // 4. 组装编辑器配置 MapString, Object editorConfig onlyOfficeService.buildEditorConfig( file, documentKey, serverBaseUrl); // 5. 整体签名OnlyOffice 要求 config 外层带 token String token OnlyOfficeJwtUtil.createToken(editorConfig); editorConfig.put(token, token); return editorConfig; } private String buildBaseUrl(HttpServletRequest request) { // 从请求头里取协议和域名拼接出基础地址 String scheme request.getHeader(X-Forwarded-Proto); if (scheme null) { scheme request.getScheme(); } String host request.getHeader(X-Forwarded-Host); if (host null) { host request.getHeader(Host); } return scheme :// host; } }buildBaseUrl这段代码值得一提。直接request.getRequestURL()在 HTTP 环境下没问题但一旦经过 Nginx 反向代理或 HTTPS 终止层拿到的协议和域名可能是内部的。所以我优先从X-Forwarded-Proto和X-Forwarded-Host头取外部域名Spring Boot 那边要确保后端开启了。buildEditorConfig内部做的事情就是拼装 Map和微信支付/支付宝签名那种套路一模一样就是把一堆参数丢到一个 Map 里然后整体当 claims 签 JWT。关键参数如下document.fileType文件扩展名去掉点如 docx、xlsx。document.key文档唯一标识前面讲过规则。document.url下载地址拼接成服务器基础地址 /api/editor/download/{fileId}。document.permissions.edit是否允许编辑预览模式设 false。editorConfig.callbackUrl回调地址。editorConfig.modeedit是编辑模式view是只读模式。editorConfig.langzh-CN。editorConfig.user.id和user.name当前登录用户的 ID 和显示名会显示在编辑器右上角多人协同时用来区分谁在编辑。4.3 下载接口实现下载接口要特别注意响应头的设置。OnlyOffice 拉取文件时是根据 URL 去请求的我们要保证两点一是返回原始文件二进制流二是Content-Type正确、Content-Disposition里的文件名编码正确。GetMapping(/download/{fileId}) public ResponseEntityResource download(PathVariable Long fileId) throws IOException { FileInfo file fileInfoService.getById(fileId); if (file null) { return ResponseEntity.notFound().build(); } Path filePath Paths.get(file.getStoragePath()); Resource resource new FileSystemResource(filePath); if (!resource.exists()) { return ResponseEntity.notFound().build(); } String encodedFileName URLEncoder.encode(file.getFileName(), StandardCharsets.UTF_8) .replace(, %20); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, attachment; filename\ encodedFileName \) .contentType(MediaType.parseMediaType( Optional.ofNullable(file.getContentType()) .orElse(application/octet-stream))) .body(resource); }这里有两个细节容易被忽略。第一文件名编码。中文文件名如果直接放进Content-Disposition很多环境会乱码我用URLEncoder标准编码后浏览器和 OnlyOffice 都能正确还原。第二Content-Type千万别统一填application/octet-stream有的版本 OnlyOffice 判断文件类型不完全靠扩展名也会参考响应头类型错了可能触发转换失败。数据库里存了contentType就直接用没存的话根据扩展名去映射一个。4.4 回调接口实现回调接口是 OnlyOffice 主动调我们的接口场景是用户保存、关闭或强制保存时把编辑后的文件内容或者一个可下载的 URL回传给我们。整个接入过程中最重要的就是它很多教程只写到配置和下载就停了实际生产里 90% 的保存不上文件没更新问题都出在这个接口。回调请求体是一个 JSON核心字段是status和url。status 的取值为1有人正在编辑通知性质不用处理。2准备保存此时url字段指向编辑后的临时文件地址。3强制保存成功url同样可下载。6编辑会话结束有新版本要保存。7强制保存完成。4 和 8对应保存失败。业务上通常只需要关心status 2 || status 6 || status 7也就是有新文件内容可以拿的时机。收到这些状态时用代码去请求url字段指向的地址下载内容后覆盖存储并更新文件的版本号和哈希。回调接口还要处理 JWT 验签。新版 OnlyOffice 会把 token 放在Authorization头里旧版本放在请求体的token字段兼容写法是两头都检查PostMapping(/callback/{fileId}) public Integer callback(PathVariable Long fileId, RequestBody(required false) MapString, Object body, RequestHeader(value Authorization, required false) String authHeader, HttpServletRequest request) { // 1. 提取并验证 JWT String token null; if (authHeader ! null authHeader.startsWith(Bearer )) { token authHeader.substring(7); } if (token ! null body ! null body.get(token) ! null) { token (String) body.get(token); } Claims claims; try { claims OnlyOfficeJwtUtil.parseToken(token); } catch (Exception e) { log.error(JWT校验失败, fileId{}, fileId, e); return 1; } // JWT 的 payload 里才是真正的回调业务数据 MapString, Object payload (MapString, Object) claims.get(payload); Integer status (Integer) payload.get(status); String downloadUrl (String) payload.get(url); if (status 2 || status 6 || status 7) { if (StringUtils.hasText(downloadUrl)) { fileInfoService.saveNewVersionFromUrl(fileId, downloadUrl); } } // 返回 0 表示处理成功OnlyOffice 会停止重试 return 0; }saveNewVersionFromUrl里的逻辑就是用 HTTP 客户端OkHttp 或者 Spring 的 RestTemplate去请求downloadUrl拿到流之后写到新的文件路径同时更新数据库里的存储路径、版本号加一、重新计算内容哈希最后把旧的物理文件删掉。这里有个权衡如果保留历史版本就不删旧文件只是把当前版本指向新文件如果业务不需要历史归档直接覆盖即可。我建议至少保留最近一版因为在线编辑出问题比如用户误删全选后保存还算常见有一版回滚兜底会安心很多。回调返回值的语义也要说清楚返回 HTTP 200 且 body 为 0OnlyOffice 认为保存成功返回 1 或者其他 HTTP 错误码OnlyOffice 会按策略重试。如果业务保存频繁失败可以观察有没有反复重试的动作。4.5 安全性补充回调与下载的鉴权前面提到下载和回调都暴露了 HTTP 接口。内网部署时风险可控但如果服务要暴露到公网必须加防护。我的做法是下载接口要求登录态从请求的 session 或者 token 里解析当前用户校验对这份文件是否有权限回调接口则校验来源 IP 段加 JWT 双重限制。OnlyOffice 回调的 JWT 是它用同一个密钥签的我们验签即可确认来源。而下载接口如果也要求 JWT需要前端在拼接 url 时带上签名参数这个方案更严谨但会引入额外复杂度前期可以先用登录态校验顶住后面再升级。5. 常见问题与避坑经验5.1 问题速查表把项目里和后端同行交流时最常出现的几类问题整理成了一张速查表遇到问题直接对着查现象可能原因处理方法编辑器一直转圈加载不出来下载地址是 localhost 或容器内不可达把 url 改成局域网 IP 或域名测试时从容器内 curl 验证打开后报 403JWT 密钥不一致或 token 过期核对后端配置和 Docker 环境变量是否一致编辑完点保存没反应日志无回调回调地址不可达确认 OnlyOffice 容器能访问到 Spring Boot 的回调端口回调收到但文件内容还是旧的下载 URL 下载失败或覆盖逻辑没走单独 curl 下载 URL 看返回码检查存储路径权限中文文件名在编辑器里乱码Content-Disposition 未正确编码按上一节写法对文件名做 URL 编码历史版本不是连续的是重新开始的文档 key 每次打开都不一样用内容哈希版本号保证同一文档 key 稳定用户点关闭编辑器立刻结束导致丢失forcesave 默认关闭前端配置加customization.forcesave: true或调短自动保存间隔大文档首次打开非常慢转换服务性能不足或网络带宽问题检查服务器资源给文档服务器单独配高性能磁盘第三行那个保存没反应是最坑的。因为用户在前端点击保存OnlyOffice 成功后大多只在前端提示我们的系统里没有任何感知想排查最直接的办法是去看 OnlyOffice 容器日志docker logs -f onlyoffice-documentserver或者直接查文档服务器保存过的痕迹。如果完全没有任何回调进来的迹象基本可以断定就是网络路径问题这个方向错了后面全白查。5.2 我踩过的几个坑第一个坑把下载地址写成了http://localhost:8080/...。浏览器端打开没问题但文档服务器是在容器里运行的它的localhost是它自己不是宿主机于是它顺着这个地址去下载文件永远下载不到编辑器就一直转圈。解决方式代码里根据请求头动态拼出外部可访问的域名或者配置一个全局的onlyoffice.external-url。第二个坑文档 key 用文件 ID 一个字段。同一份文件被编辑一次后第二次打开发现历史记录还是零朝。原因就是 key 没变OnlyOffice 认为还是第一次那次会话直接覆盖了。改成内容哈希版本号后每次保存成功版本号加一key 就变了历史记录就正常了。但要注意key 不能每次打开都变否则历史永远断档。这里的度就是内容变了才变没变不变。第三个坑JWT 验签时没处理旧版本兼容。OnlyOffice 升级前后回调里 token 的携带位置不一样我第一版只从Authorization头取结果老的文档服务器回调全都验签失败。后来改成两头都取问题解决。第四个坑回调保存时直接覆盖原文件没有备份。一次测试中一个同事全选了内容删掉又保存原文档全没了还好是测试环境。从那以后我在保存逻辑里默认保留上一个版本文件保留策略配到配置项里生产环境默认至少保留 3 个版本。5.3 性能与安全优化建议最后说点上线前值得做的事情。性能方向文档服务器对 CPU 敏感转换服务docx 转渲染格式是纯 CPU 密集操作并发高的时候观察负载该加资源就加资源。存储方面把/data/onlyoffice挂到 SSD 上首次打开大文档的等待时间会明显下降。另外如果你的系统大部分用户只是偶尔编辑同一个文件可以考虑把自动保存间隔调成 5 分钟减少文档服务器的写入压力。安全方向生产环境务必用 HTTPS 访问文档服务器JWT 密钥要放到配置中心管理定期轮换下载接口和回调接口做来源校验回调接口白名单到已知网段。还有就是定期清理文档服务器的缓存目录长时间运行的容器缓存会越来越大不清的话磁盘迟早报警。最后是版本策略。OnlyOffice 版本升级跨度大的时候比如从 7.x 升到 8.x配置参数和回调状态码可能有细微变化务必先在测试环境完整跑一遍部署新镜像 - 打开 - 编辑 - 保存 - 验证文件内容的链路确认无问题再上生产。这种事我吃过亏所以现在每次升级都按这个流程走。如果让我总结这套集成方案最核心的经验就一句话把网络访问关系先画清楚、把三个角色的职责先搞明白再去写代码后面几乎不会遇到难题。反而是那些教程里一笔带过的地址、密钥、回调状态码这些细枝末节才是真正决定成败的地方。你只要按着这个链路亲手跑一遍部署和最小集成后面加功能、换存储、接 OSS 都是水到渠成的事。