技术篇-文档在线编辑
# 一、文档编辑器需要有哪些能力
- 在线编辑:支持多人实时协作编辑,兼容多种文档格式(如DOCX、XLSX、PPTX等)。
- 版本管理:自动保存文档版本,支持版本回滚和对比。
- 实时协作:支持多用户同时在线编辑合同,提升团队协作效率。
- 提升效率:无需下载上传,直接在系统中编辑,简化流程。
- 插件扩展:提供丰富的API接口,支持开发者自定义插件,实现与第三方系统的深度集成。
# 二、不同文本编辑器的对比
对比维度 | OnlyOffice | WPS | 永中Office | 金格插件 |
---|---|---|---|---|
价格 | 有社区版免费,大于20并发需购买许可证,价格低 | 有SAAS版本,私有化版本价格较高(数十万起步) | 需购买许可证,私有化价格较低 | 需购买许可证,主要面向企业级用户 |
文档兼容性 | 较好 | 最好 | 一般 | 复杂排版支持有限 |
协作功能 | 较强 | 最强 | 较弱 | 无 |
对接能力 | 支持插件开发,用插件实现对接 | 官方提供文档,支持第三方对接 | 官方提供文档,支持第三方对接 | 支持第三方对接 |
私有化部署 | 支持 | 支持 | 支持 | 支持 |
适用场景 | 小企业可以免费使用,中型企业费用也低 | 适合有国产化要求的中大型企业内部搭建文档中台 | 适合对国产化有要求,但预算有限的企业 | 适合对合同编辑要求较低的企业 |
# 三、对接OnlyOffice
基于项目预算和实际需求,我们最终选择了OnlyOffice作为合同管理系统中的文档编辑工具。
# 3.1 对接流程
- 部署 OnlyOffice 文档服务:通过 Docker 部署 OnlyOffice Document Server。
- 配置前端编辑器:在前端页面中引入 OnlyOffice 的 API,并初始化编辑器。
- Java 后端实现文件管理:
- 提供文件上传和下载接口。
- 实现回调接口,用于保存编辑后的文件。
- 配置编辑器参数:通过 JSON 配置编辑器的行为,如文件类型、编辑权限、回调地址等。
# 3.2 部署OnlyOffice
我们使用docker来部署OnlyOffice.命令如下:
sudo docker run -i -t -d -p 9000:80 -v /home/myOnlyOffice:/var/www/onlyoffice/documentserver/web-apps/wsData --env JWT_SECRET=zxcm -e JWT_ENABLED=true onlyoffice/documentserver:7.5.1
1
参数说明:
--env JWT_SECRET=zxcm
- 作用:设置环境变量
JWT_SECRET
,用于 JWT(JSON Web Token)加密。 - 说明:
JWT_SECRET
是 OnlyOffice 用于验证请求的密钥。zxcm
是您自定义的密钥,建议使用强随机字符串。
- 作用:设置环境变量
-e JWT_ENABLED=true
- 作用:设置环境变量
JWT_ENABLED
,启用 JWT 验证。 - 说明:
JWT_ENABLED=true
表示启用 JWT 验证,确保 OnlyOffice 的 API 请求经过身份验证。- 如果设置为
false
,则禁用 JWT 验证(不推荐)。
- 作用:设置环境变量
onlyoffice/documentserver:7.5.1
- 作用:指定要运行的 Docker 镜像及其版本。
- 说明:
onlyoffice/documentserver
:OnlyOffice 的官方镜像名称。7.5.1
:镜像的版本号,确保使用特定版本的 OnlyOffice。
# 3.3 后端对接OnlyOffice
# (1)文件下载接口
- 下载接口:根据文件 ID 返回文件的 URL,供 OnlyOffice 加载文件。
示例代码:
@GetMapping("/download/{fileId}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileId) {
// 根据 fileId 获取文件路径
File file = new File("/path/to/store/" + fileId);
Resource resource = new FileSystemResource(file);
return ResponseEntity.ok().body(resource);
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# (2)回调接口实现
OnlyOffice 在编辑完成后会调用回调接口,Java 后端需要处理文件保存逻辑。
示例代码:
@PostMapping("/callback")
public void saveFile(HttpServletRequest request, HttpServletResponse response) throws IOException {
PrintWriter writer = response.getWriter();
Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
String body = scanner.hasNext() ? scanner.next() : "";
JSONObject jsonObject = JSONObject.parseObject(body);
if (jsonObject.getInteger("status") == 2) {
String fileUrl = jsonObject.getString("url");
// 下载文件并保存到服务器
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream inputStream = connection.getInputStream();
Files.copy(inputStream, Paths.get("/path/to/store/edited_file.docx"));
connection.disconnect();
}
writer.write("{\"error\":0}");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 3.4 前端对接OnlyOffice
在前端页面中引入 OnlyOffice 的 API 并初始化编辑器:
<script type="text/javascript" src="http://<your-server-ip>:9000/web-apps/apps/api/documents/api.js"></script>
<div id="editorDiv"></div>
<script>
var config = {
document: {
fileType: "docx",
key: "unique_file_key",
title: "Example Document",
url: "http://<your-server-ip>/path/to/file.docx",
permissions: {
edit: true,
download: true,
print: true
}
},
documentType: "text",
editorConfig: {
callbackUrl: "http://<your-server-ip>/callback",
lang: "zh",
user: {
id: "user_id",
name: "User Name"
}
}
};
new DocsAPI.DocEditor("editorDiv", config);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
key
:文件的唯一标识,用于区分不同文件。callbackUrl
:编辑完成后,OnlyOffice 会调用此接口保存文件。
# 四、OnlyOffice插件开发与通信
# 4.1 插件开发参考资料
• 官方插件开发文档:ONLYOFFICE API (opens new window)
• 示例代码库:GitHub ONLYOFFICE Plugins (opens new window)
# 4.2 前端与插件交互
通信原理
ONLYOFFICE编辑器以iframe形式嵌入页面,通过HTML5的postMessage
协议实现跨域通信。消息发送示例
const iframe = document.getElementById('onlyoffice-frame'); const dataMessage = { frameEditorId: "iframeEditor", guid: "asc.{插件唯一ID}", // 需与config.json一致 type: "onExternalPluginMessage", data: { type: "insertText", text: "动态插入内容" } }; iframe.contentWindow.postMessage(JSON.stringify(dataMessage), '*');
1
2
3
4
5
6
7
8插件端消息监听
在pluginCode.js
中实现:window.Asc.plugin.onExternalPluginMessage = function(data) { if (data.type === "insertText") { this.executeCommand("insertText", { text: data.text }); } // 扩展其他类型如插入书签、内容块 };
1
2
3
4
5
6
上次更新: 2025/03/19, 17:34:25