跳到主要内容OnlyOffice 私有化部署与 Spring Boot 整合实战教程 | 极客日志Javajava
OnlyOffice 私有化部署与 Spring Boot 整合实战教程
在 Windows 11 环境下通过 Docker 私有化部署 OnlyOffice Document Server,并结合 Spring Boot 实现文档预览与编辑功能。流程包括镜像拉取、容器参数配置、Java 后端接口开发、前端 SDK 集成及常见问题排查,重点解决跨域访问、回调保存及网络连通性问题,提供可直接运行的代码示例与测试步骤。
CoderByte28 浏览 OnlyOffice 私有化部署(Win11 + Docker)+ Java 对接全流程教程
一、环境前提
- Win11 已安装 Docker Desktop(需确保 Docker 服务正常运行,可通过
docker -v 验证)
- 网络通畅(需拉取 OnlyOffice 镜像)
- JDK 8+(Java 对接用)、Maven(可选,管理依赖)
- 端口说明:OnlyOffice 默认占用 80/443,本次用 9999 映射(避免端口冲突)
二、OnlyOffice Docker 部署(核心步骤)
步骤 1:拉取最新版 OnlyOffice 镜像
打开 Win11 的「终端」(管理员模式),执行以下命令拉取官方最新镜像:
docker pull onlyoffice/documentserver:latest
- 验证拉取结果:执行
docker images,能看到 onlyoffice/documentserver 镜像即成功。
步骤 2:创建本地挂载目录(持久化数据)
为了避免容器重启后数据丢失,创建本地目录映射容器内数据目录:
mkdir D:\onlyoffice\data
mkdir D:\onlyoffice\logs
mkdir D:\onlyoffice\plugins
mkdir D:\onlyoffice\fonts
步骤 3:运行 OnlyOffice 容器(忽略 Token 验证)
核心命令:关闭 JWT 验证(忽略 token),并映射端口和目录:
docker run -itd --name onlyoffice \
-p 9999:80 \
-v D:\onlyoffice\data:/var/www/onlyoffice/Data \
-v D:\onlyoffice\logs:/var/log/onlyoffice \
-v D:\onlyoffice\plugins:/var/www/onlyoffice/documentserver/sdkjs-plugins \
-v D:\onlyoffice\fonts:/usr/share/fonts \
-e JWT_SECRET=mysecret \
-e JWT_ENABLED=false \
--restart=always \
onlyoffice/documentserver:latest
参数说明:
-p 9999:80:将容器 80 端口映射到主机 9999 端口(访问用)
-v:目录挂载(数据/日志/插件/字体持久化)
JWT_ENABLED=false:关键!关闭 Token 验证(忽略 JWT)
--restart=always:Docker 重启后自动启动容器
--name onlyoffice:给容器命名,方便管理
步骤 4:验证 OnlyOffice 是否启动成功
- 若状态异常,执行
docker logs onlyoffice 查看报错(常见问题:端口被占用,换 -p 9998:80 重试)。
- 进阶验证:访问
http://localhost:9999/healthcheck,返回 即服务正常。
{"status":"true"}
浏览器访问验证:打开 http://localhost:9999,若看到 OnlyOffice 欢迎页(显示 Document Server is running),则部署成功!
查看容器状态:执行 docker ps,若 onlyoffice 状态为 Up 则运行中;
步骤 5:解决启动慢/依赖缺失问题(Win11 常见)
OnlyOffice 启动需要下载依赖,Win11 可能出现启动超时:
- 执行
docker exec -it onlyoffice bash 进入容器;
supervisorctl restart all
三、Java 对接 OnlyOffice 实现简单实例
核心逻辑
Java 后端提供「文档预览/编辑」接口,返回 OnlyOffice 所需的配置参数(文档地址、回调地址等),前端通过 OnlyOffice SDK 加载编辑器。
步骤 1:创建 Spring Boot 项目(Maven)
pom.xml 依赖(核心)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>onlyoffice-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
步骤 2:配置文件(application.yml)
server:
port: 48083
onlyOffice:
server:
url: http://localhost:9999
document:
storage: D:/onlyoffice/documents/
步骤 3:核心工具类(文档管理)
创建 DocumentUtils.java,处理文档路径、URL 生成:
package com.example.onlyofficedemo.utils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.File;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@Component
public class OnlyOfficeUtils {
@Value("${onlyoffice.document.storage}")
private String documentStoragePath;
@Value("${onlyoffice.server.url}")
private String onlyOfficeServerUrl;
@Value("${server.port}")
private String serverPort;
public void initStorageDir() {
File dir = new File(documentStoragePath);
if (!dir.exists()) {
dir.mkdirs();
}
}
public String getDocumentUrl(String fileName) {
String host = "http://" + getLocalIp() + ":" + serverPort;
return host + "/document/get?fileName=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8);
}
public String getCallbackUrl(String fileName) {
String host = "http://" + getLocalIp() + ":" + serverPort;
return host + "/document/callback?fileName=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8);
}
public String getEditorUrl() {
return onlyOfficeServerUrl + "/web-apps/apps/api/documents/api.js";
}
private String getLocalIp() {
try {
java.net.InetAddress addr = java.net.InetAddress.getLocalHost();
return addr.getHostAddress();
} catch (Exception e) {
return "127.0.0.1";
}
}
public String getDocumentLocalPath(String fileName) {
return documentStoragePath + File.separator + fileName;
}
}
步骤 4:Controller 层(核心接口)
创建 OnlyOfficeOffController.java,提供「文档获取、编辑配置、回调保存」接口:
package com.vintechhk.module.document.controller.app.document;
import com.alibaba.fastjson.JSONObject;
import com.vintechhk.module.document.util.OnlyOfficeUtils;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.UUID;
@Tag(name = "用户端 - 在线文档")
@RestController
@RequestMapping("/doc/only-office")
@Validated
@Slf4j
@CrossOrigin(origins = "*")
public class OnlyOfficeOffController {
@Autowired
private OnlyOfficeUtils onlyOfficeUtils;
@GetMapping("/init")
public String initTestDocument() {
log.info("-----------------------初始化---init-------1-----------");
onlyOfficeUtils.initStorageDir();
String testFileName = "test-" + UUID.randomUUID() + ".docx";
String localPath = onlyOfficeUtils.getDocumentLocalPath(testFileName);
File testFile = new File(localPath);
if (!testFile.exists()) {
try {
testFile.createNewFile();
FileOutputStream fos = new FileOutputStream(testFile);
fos.write("OnlyOffice Java 对接测试文档".getBytes());
fos.close();
} catch (Exception e) {
return "初始化失败:" + e.getMessage();
}
}
log.info("-----------------------初始化---init------2------------");
return "测试文档创建成功!文件名:" + testFileName + ",访问编辑页:http://localhost:48083/document/edit?fileName=" + testFileName;
}
@GetMapping("/get")
public ResponseEntity<FileSystemResource> getDocument(@RequestParam String fileName) {
String localPath = onlyOfficeUtils.getDocumentLocalPath(fileName);
File file = new File(localPath);
if (!file.exists()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Content-Disposition", "attachment; filename=" + fileName);
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
return ResponseEntity.ok().headers(headers).contentLength(file.length()).contentType(MediaType.APPLICATION_OCTET_STREAM).body(new FileSystemResource(file));
}
@GetMapping("/edit")
public JSONObject getEditConfig(@RequestParam String fileName) {
JSONObject config = new JSONObject();
config.put("documentType", "text");
config.put("editorUrl", onlyOfficeUtils.getEditorUrl());
JSONObject document = new JSONObject();
document.put("title", fileName);
document.put("url", onlyOfficeUtils.getDocumentUrl(fileName));
document.put("fileType", fileName.substring(fileName.lastIndexOf(".") + 1));
document.put("key", UUID.randomUUID().toString());
config.put("document", document);
JSONObject editorConfig = new JSONObject();
editorConfig.put("callbackUrl", onlyOfficeUtils.getCallbackUrl(fileName));
editorConfig.put("lang", "zh-CN");
config.put("editorConfig", editorConfig);
return config;
}
@PostMapping("/callback")
public String callback(HttpServletRequest request, @RequestParam String fileName) {
try {
JSONObject json = JSONObject.parseObject(request.getInputStream(), "UTF-8".getClass());
String status = json.getString("status");
if ("2".equals(status)) {
String downloadUrl = json.getJSONObject("url").getString("url");
URL url = new URL(downloadUrl);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
String localPath = onlyOfficeUtils.getDocumentLocalPath(fileName);
FileOutputStream fos = new FileOutputStream(localPath);
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.close();
is.close();
}
return "{\"error\":0}";
} catch (Exception e) {
return "{\"error\":1,\"message\":\"" + e.getMessage() + "\"}";
}
}
}
步骤 5:启动类
创建 OnlyOfficeDemoApplication.java:
package com.example.onlyofficedemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OnlyOfficeDemoApplication {
public static void main(String[] args) {
SpringApplication.run(OnlyOfficeDemoApplication.class, args);
}
}
步骤 6:前端页面(编辑页)
在 resources/static 目录下创建 edit.html(Spring Boot 静态资源目录):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>OnlyOffice 编辑测试</title>
<style>
#editor { width: 100%; height: 800px; }
</style>
</head>
<body>
<div id="editor"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
function getUrlParam(name) {
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
let r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
let fileName = getUrlParam("fileName");
$.get("/document/edit?fileName=" + fileName, function(data) {
let script = document.createElement('script');
script.src = data.editorUrl;
script.onload = function() {
new DocsAPI.DocEditor("editor", {
document: data.document,
documentType: data.documentType,
editorConfig: data.editorConfig,
width: "100%",
height: "100%"
});
};
document.body.appendChild(script);
});
</script>
</body>
</html>
四、全流程测试
步骤 1:启动服务
- 确保 Docker 中的 OnlyOffice 容器运行(
docker start onlyoffice);
- 启动 Spring Boot 项目(运行
OnlyOfficeDemoApplication.java);
- 提前创建
D:/onlyoffice/documents/ 目录(文档存储用)。
步骤 2:初始化测试文档
浏览器访问:http://localhost:48083/document/init
返回类似:测试文档创建成功!文件名:test-xxx.docx,访问编辑页:http://localhost:48083/document/edit?fileName=test-xxx.docx
步骤 3:访问编辑页
复制返回的编辑页 URL 到浏览器打开,即可看到 OnlyOffice 编辑器加载完成,显示测试文档:
- 可编辑文档内容(如输入「测试 OnlyOffice 保存功能」);
- 关闭编辑器/点击保存,OnlyOffice 会回调
http://localhost:48083/document/callback 接口,将编辑后的内容保存到本地文档。
步骤 4:验证保存结果
打开 D:/onlyoffice/documents/ 下的测试文档,查看内容是否已更新,确认保存成功。
五、常见问题解决
1. OnlyOffice 无法加载文档
- 原因:Java 服务的 IP 用了
localhost,容器内无法访问主机的 localhost;
- 解决:
DocumentUtils.java 中 getLocalIp() 需返回 Win11 实际 IP(如 192.168.1.100),而非 127.0.0.1。
2. 容器启动后访问 8080 端口超时
- 原因:OnlyOffice 启动需要下载依赖,Win11 网络慢;
- 解决:执行
docker exec -it onlyoffice bash 进入容器,运行 apt update && apt install -y wget,再重启容器。
3. 回调保存失败
- 原因:OnlyOffice 容器无法访问 Java 服务的回调地址;
- 解决:确保 Java 服务的端口(9000)未被防火墙拦截,且回调地址用主机实际 IP。
4. 编辑器显示乱码
- 原因:文档编码问题;
- 解决:Java 写入文档时用 UTF-8 编码,前端页面指定
<meta charset="UTF-8">。
六、总结
- Docker 部署核心:关闭 JWT 验证(
JWT_ENABLED=false),映射端口和目录;
- Java 对接核心:提供「文档访问 URL」和「回调保存 URL」,且 URL 需用主机实际 IP;
- 测试流程:初始化文档 → 访问编辑页 → 编辑保存 → 验证本地文档更新。
至此,从 OnlyOffice 私有化部署到 Java 对接测试的全流程已完成,可基于此扩展更多功能(如文档权限控制、多格式支持、历史版本等)。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online