当前位置: 首页 > news >正文

Docker 部署onlyoffice

以下是全面优化后的 OnlyOffice 部署、Token 生成、前端测试及文件转换方案,解决潜在问题、提升稳定性和易用性:

一、Docker 部署优化(稳定性+可维护性)

sudo docker run -itd \-p 1230:80 \--name onlyoffice \--restart=always \  # 容器异常自动重启--privileged=true \  # 解决文件权限问题-e JWT_SECRET=VI71S3cGtX75g96HgFW52785zQhblz1KwMc1Jzk \-e JWT_ENABLED=true \-e TZ=Asia/Shanghai \  # 同步时区,避免日志时间错乱-v /data/middleware/onlyoffice/DocumentServer/logs:/var/log/onlyoffice \-v /data/middleware/onlyoffice/DocumentServer/data:/var/www/onlyoffice/Data \-v /data/middleware/onlyoffice/DocumentServer/lib:/var/lib/onlyoffice \-v /data/middleware/onlyoffice/DocumentServer/rabbitmq:/var/lib/rabbitmq \-v /data/middleware/onlyoffice/DocumentServer/redis:/var/lib/redis \-v /data/middleware/onlyoffice/DocumentServer/db:/var/lib/postgresql \onlyoffice/documentserver:7.3

优化点

  1. 新增 --restart=always 确保服务高可用
  2. 增加 --privileged=true 解决宿主机与容器文件权限不匹配问题
  3. 配置 TZ=Asia/Shanghai 同步时区,日志时间更易排查
  4. 格式化命令换行,提升可读性

二、Token 生成工具类优化(健壮性+易用性)

package org.example;import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Objects;/*** OnlyOffice JWT Token 生成工具(优化版)* 支持预览/编辑/转换三种模式,新增参数校验、异常处理、可配置化*/
public class OnlyOfficeTokenGenerator {// 可配置常量(集中管理,便于修改)private static final String JWT_SECRET = "VI71S3cGtX75g96HgFW52785zQhblz1KwMc1Jzk";private static final ObjectMapper MAPPER = new ObjectMapper();private static final String ALGORITHM = "HS256";private static final String TYPE = "JWT";private static final int EXPIRATION_SECONDS = 3600; // 1小时有效期private static final String DEFAULT_LANG = "zh-CN"; // 默认中文// 单例模式(避免重复创建对象)private static final OnlyOfficeTokenGenerator INSTANCE = new OnlyOfficeTokenGenerator();private OnlyOfficeTokenGenerator() {}public static OnlyOfficeTokenGenerator getInstance() { return INSTANCE; }public static void main(String[] args) {try {// 配置参数(替换为实际值)String docUrl = "http://101.42.40.19:9000/xingyue-knowledge/工作需要1.0.docx";String docTitle = "工作需要1.0.docx";String fileType = "docx";String callbackUrl = "https://xingyue.asia/test/callback";// 生成 TokenString viewToken = INSTANCE.generateViewToken(docUrl, docTitle, fileType, "word");System.out.println("预览 Token:\n" + viewToken + "\n");String editToken = INSTANCE.generateEditToken(docUrl, docTitle, fileType, "word", callbackUrl, "测试用户", "user123");System.out.println("编辑 Token:\n" + editToken + "\n");String convertToken = INSTANCE.generateConvertToken(docUrl, fileType, "pdf", "convert_"+System.currentTimeMillis(), docTitle);System.out.println("转换 Token:\n" + convertToken);} catch (Exception e) {System.err.println("Token 生成失败:" + e.getMessage());e.printStackTrace();}}/*** 预览模式 Token(仅查看,无编辑权限)*/public String generateViewToken(String docUrl, String docTitle, String fileType, String documentType) throws Exception {validateRequiredParams(new StringParam(docUrl, "文档URL"),new StringParam(docTitle, "文档标题"),new StringParam(fileType, "文件类型"),new StringParam(documentType, "文档类别"));return generateJWT(createDocumentPayload("view", docUrl, docTitle, fileType, documentType, null, null, null));}/*** 编辑模式 Token(支持修改+回调)*/public String generateEditToken(String docUrl, String docTitle, String fileType, String documentType,String callbackUrl, String userName, String userId) throws Exception {validateRequiredParams(new StringParam(docUrl, "文档URL"),new StringParam(docTitle, "文档标题"),new StringParam(fileType, "文件类型"),new StringParam(documentType, "文档类别"),new StringParam(callbackUrl, "回调地址"),new StringParam(userName, "用户名"),new StringParam(userId, "用户ID"));return generateJWT(createDocumentPayload("edit", docUrl, docTitle, fileType, documentType, callbackUrl, userName, userId));}/*** 文件转换 Token(支持 docx→pdf 等格式转换)*/public String generateConvertToken(String docUrl, String fileType, String outputType, String convertKey, String title) throws Exception {validateRequiredParams(new StringParam(docUrl, "文档URL"),new StringParam(fileType, "源文件类型"),new StringParam(outputType, "目标文件类型"),new StringParam(convertKey, "转换标识Key"),new StringParam(title, "文件标题"));return generateJWT(createConvertPayload(docUrl, fileType, outputType, convertKey, title));}/*** 通用 JWT 生成逻辑(优化 Base64 编码、签名算法稳定性)*/private String generateJWT(Map<String, Object> payload) throws Exception {// 构建 HeaderMap<String, String> header = new HashMap<>(2);header.put("alg", ALGORITHM);header.put("typ", TYPE);// JSON 序列化(避免空值导致的解析异常)String headerJson = MAPPER.writeValueAsString(header);String payloadJson = MAPPER.writeValueAsString(payload);// Base64URL 编码(严格遵循 JWT 标准)String headerBase64 = base64UrlEncode(headerJson.getBytes(StandardCharsets.UTF_8));String payloadBase64 = base64UrlEncode(payloadJson.getBytes(StandardCharsets.UTF_8));// 生成签名(避免密钥为空)if (JWT_SECRET == null || JWT_SECRET.trim().isEmpty()) {throw new IllegalArgumentException("JWT_SECRET 不能为空");}String signature = hmacSha256(headerBase64 + ".", JWT_SECRET);return headerBase64 + "." + payloadBase64 + "." + signature;}/*** 构建文档操作 Payload(预览/编辑)*/private Map<String, Object> createDocumentPayload(String mode, String docUrl, String docTitle, String fileType,String documentType, String callbackUrl, String userName, String userId) {Map<String, Object> payload = new HashMap<>();payload.put("document", createDocumentInfo(docUrl, docTitle, fileType));payload.put("editorConfig", createEditorConfig(mode, callbackUrl, userName, userId));payload.put("documentType", documentType);// 设置过期时间(避免 Token 永久有效)long currentTime = System.currentTimeMillis() / 1000;payload.put("iat", currentTime);payload.put("exp", currentTime + EXPIRATION_SECONDS);return payload;}/*** 构建文件转换 Payload*/private Map<String, Object> createConvertPayload(String docUrl, String fileType, String outputType, String convertKey, String title) {Map<String, Object> payload = new HashMap<>();payload.put("async", false); // 同步转换(立即返回结果)payload.put("filetype", fileType);payload.put("key", convertKey);payload.put("outputtype", outputType);payload.put("title", title);payload.put("url", docUrl);// 设置过期时间long currentTime = System.currentTimeMillis() / 1000;payload.put("iat", currentTime);payload.put("exp", currentTime + EXPIRATION_SECONDS);return payload;}/*** 构建文档基础信息(优化文档 Key 生成逻辑)*/private Map<String, Object> createDocumentInfo(String docUrl, String docTitle, String fileType) {Map<String, Object> document = new HashMap<>();// 文档 Key:基于 URL + 时间戳生成(避免同一文档多次打开冲突)String docKey = Base64.getUrlEncoder().withoutPadding().encodeToString((docUrl + System.currentTimeMillis()).getBytes());document.put("fileType", fileType);document.put("key", docKey);document.put("title", docTitle);document.put("url", docUrl);return document;}/*** 构建编辑器配置*/private Map<String, Object> createEditorConfig(String mode, String callbackUrl, String userName, String userId) {Map<String, Object> editorConfig = new HashMap<>();editorConfig.put("mode", mode);editorConfig.put("lang", DEFAULT_LANG); // 默认中文// 编辑模式额外配置if ("edit".equals(mode)) {editorConfig.put("callbackUrl", callbackUrl);editorConfig.put("canDownload", true); // 允许下载文档editorConfig.put("canPrint", true); // 允许打印文档// 用户信息Map<String, Object> user = new HashMap<>();user.put("name", userName);user.put("id", userId);editorConfig.put("user", user);}return editorConfig;}/*** 基础工具类:Base64URL 编码(遵循 JWT 标准,去除 = 填充)*/private String base64UrlEncode(byte[] data) {return Base64.getUrlEncoder().withoutPadding().encodeToString(data);}/*** 基础工具类:HMAC-SHA256 签名*/private String hmacSha256(String data, String secret) throws Exception {Mac mac = Mac.getInstance("HmacSHA256");SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");mac.init(secretKeySpec);byte[] signatureBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));return base64UrlEncode(signatureBytes);}/*** 参数校验工具(支持批量校验,清晰提示缺失参数)*/private static class StringParam {private final String value;private final String name;public StringParam(String value, String name) {this.value = value;this.name = name;}public boolean isInvalid() { return value == null || value.trim().isEmpty(); }public String getName() { return name; }}private void validateRequiredParams(StringParam... params) {StringBuilder errorMsg = new StringBuilder();for (StringParam param : params) {if (param.isInvalid()) {errorMsg.append(param.getName()).append("、");}}if (errorMsg.length() > 0) {throw new IllegalArgumentException("必填参数缺失:" + errorMsg.substring(0, errorMsg.length() - 1));}}
}

优化点

  1. 采用单例模式,减少对象创建开销
  2. 新增集中式配置常量,便于维护
  3. 优化参数校验逻辑,错误提示更清晰
  4. 改进文档 Key 生成规则,避免冲突
  5. 编辑模式新增下载/打印权限配置
  6. 完善异常处理,便于问题排查
  7. 严格遵循 JWT 标准,提升兼容性

三、前端测试页优化(兼容性+用户体验)

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>OnlyOffice 文档编辑测试</title><style>* { margin: 0; padding: 0; box-sizing: border-box; }body, html { height: 100%; overflow: hidden; }#placeholder { height: 100vh; width: 100vw; }/* 加载提示样式 */.loading {position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);font-size: 18px; color: #333; z-index: 9999;}</style>
</head>
<body><div class="loading">文档加载中...</div><div id="placeholder"></div><!-- 优先加载本地 OnlyOffice API(避免跨域/网络问题) --><script type="text/javascript" src="http://localhost:1230/web-apps/apps/api/documents/api.js"></script><script type="text/javascript">// 配置参数(替换为实际 Token 和文档地址)const config = {token: "替换为生成的编辑 Token",document: {fileType: "docx",title: "工作需要1.0.docx",url: "http://101.42.40.19:9000/xingyue-knowledge/工作需要1.0.docx"},documentType: "word",editorConfig: {callbackUrl: "https://xingyue.asia/test/callback",lang: "zh-CN",user: { name: "测试用户", id: "user123" },canDownload: true,canPrint: true},width: "100%",height: "100%",// 错误处理回调(提升用户体验)events: {onError: function(error) {console.error("OnlyOffice 错误:", error);alert(`文档加载失败:${error.message}\n请检查 Token 有效性和文档地址是否可访问`);},onReady: function() {document.querySelector(".loading").style.display = "none"; // 隐藏加载提示}}};// 确保 API 加载完成后初始化if (window.DocsAPI) {initEditor();} else {// 降级处理:API 加载失败时重试setTimeout(initEditor, 1000);}function initEditor() {try {window.docEditor = new DocsAPI.DocEditor("placeholder", config);} catch (e) {console.error("编辑器初始化失败:", e);alert("编辑器初始化失败,请刷新页面重试");}}</script>
</body>
</html>

优化点

  1. 加载本地 API 脚本,避免跨域和网络依赖
  2. 新增加载提示,提升用户体验
  3. 增加错误处理回调,便于排查问题
  4. 优化样式适配,支持全屏显示
  5. 增加 API 加载重试机制,提升稳定性

四、文件转换接口优化(安全性+可读性)

1. 转换请求(推荐用 POSTMAN/ curl 测试)

请求地址http://localhost:1230/converter(注意:OnlyOffice 转换接口默认 80 端口,对应部署的 1230 端口)
请求方法:POST
请求头

Content-Type: application/json

请求体

{"async": false,"filetype": "docx","key": "convert_1762394085","outputtype": "pdf","title": "工作需要1.0.pdf","url": "http://101.42.40.19:9000/xingyue-knowledge/工作需要1.0.docx","token": "替换为生成的转换 Token"
}
http://www.dtcms.com/a/586861.html

相关文章:

  • 高端模版网站网站优化外包服务
  • 花钱做的网站本人可以关闭吗湘潭网站建设 地址磐石网络
  • 环境配置+pytorch配置+本地部署大模型
  • 网站建设主页文档软件设计方案怎么写
  • 千山科技做网站好不好企业seo关键字优化
  • 基于Spring ApplicationEvent的业务解耦实践
  • 网站留言板作用ktv网站模板
  • 什么网站必须要flash寮步网站建设价钱
  • 手机网站建设服务电话现在做网站都是怎么做的
  • C++项目中实现无锁队列
  • 谷歌:LLM监督强化学习框架SRL
  • C++ 实现智能指针my_auto_ptr支持指针剥离及函数间传递
  • 网站建设公司如何挖掘客户少儿编程证书含金量排名
  • 电脑自带的做网站叫什么娱乐论坛网站建设方案范文
  • AI?不,是Illustrator“变量”:10分钟生成500张UI卡片
  • 南漳网站制作网站建设与网页制作案例教程
  • 宜城网站建设网络推广网站推广软件app
  • 怎样在网站上做有效的广告投放网站制作找哪个
  • wordpress 站外调用网站建设怎么找客户
  • 想做个网站要多少钱单位做网站图片素材
  • 洛阳霞光企业网站建设公司深圳网站搭建电话
  • C语言进阶:共用体和枚举
  • 网站单页源码ui培训班哪里有
  • 鸟人 网站建设东莞常平有哪些好玩的地方
  • 专做动漫解说的网站做视频采集网站犯法
  • 1.5 程序的开发过程
  • 网站推广公司排名小米路由2 做网站
  • 数据索引是无序时,直接用这个数据去画图的话,显示的图是错误的
  • 网站建设 制作公司网站建设好后能修改吗
  • CentOS7安装Docker及配置代理