我司目前使用的文档处理中心就是基于only office搭建的集群,个人觉得还是比较好用的。

1. 安装

官网:https://www.onlyoffice.com/zh/download.aspx

提供了多种安装方式,为了方便直接使用docker安装。

拉取only office镜像:

1
docker pull onlyoffice/documentserver

启动docker容器:

1
2
3
4
5
6
docker run -i -t -d -p 6831:80 --restart=always \
--name onlyoffice-document-server \
-v /app/onlyoffice/DocumentServer/data:/var/www/onlyoffice/Data \
-v /app/onlyoffice/DocumentServer/logs:/var/log/onlyoffice \
-v /app/onlyoffice/DocumentServer/lib:/var/lib/onlyoffice \
onlyoffice/documentserver

安装ftp服务:

1
docker run -d -p 21:21 -p 20:20 -p 21100-21110:21100-21110 -v F:/OnlyOfficeFile:/home/vsftpd -e FTP_USER=lingxiao -e FTP_PASS=12345678 -e PASV_ADDRESS=192.168.26.123 -e PASV_MIN_PORT=21100 -e PASV_MAX_PORT=21110 --name vsftpd --restart=always fauria/vsftpd

-e FTP_USER=davion -e FTP_PASS=davion 添加一个初始化用户davion

PASV_MIN_PORT和PASV_MAX_PORT映射的是被动模式下端口使用范围

2. 下载最佳示例

下载地址:https://api.onlyoffice.com/editors/example/java

这个示例的代码看起来还是比较繁琐的,还用的servlet那套东西,而且由于是本地使用,docker访问宿主机网络有问题,于是我自己写了一个SpringBoot版本的。

SpringBoot版本github地址:https://github.com/lingxiaoplus/offfice

修改配置

源码下载下来,需要修改yml中的几个配置:

1
2
3
4
5
6
7
office:
filesizeMax: 5242880
saveRootPath: D:/OnlyOfficeFile
baseUrl: http://localhost:6831
docService:
url:
callbackServer: http://192.168.170.113:8080/callback/office

其中saveRootPath改为你本地存在的一个目录,这个目录没啥用,只是作为一个临时文件目录。

baseUrl改为onlyoffice服务的ip+端口地址。

callbackServer修改成你本机的ip,作为回调接口,保存文件内容的时候,only office会调用,一定要确保only office能访问到你本机。不然会给出弹框提示,无法保存文件:

因为only office是装在docker中的,而docker和我本机用的不是同一个网络,only office没法下载本地的文件,所以我就直接把文件上传到七牛云oss,这里要配置一下七牛云:

1
2
3
4
5
6
7
oss:
type: qiniu
accessKey: V_ZleIlYzHPjekmAuRvDkkM12f # 设置好账号的ACCESS_KEY和SECRET_KEY
secretKey: U8SndxcceqMZGIKvhfPL3grP75cJlOH
bucketName: office # 要上传的UrlAccessDecisionManager空间
prefixDomain: http://oss.lingxiao.top
rootPath: /onlyOffice

如果没有七牛云,也可以自己用minio本地搭一个对象存储服务,同样要保证文件链接是可访问的:

1
2
3
4
5
6
7
oss:
type: minio
accessKey: admin # 设置好账号的ACCESS_KEY和SECRET_KEY
secretKey: admin123456
bucketName: office # 要上传的UrlAccessDecisionManager空间
prefixDomain: http://oss.lingxiao.top
rootPath: /onlyOffice

docker搭建minio对象存储服务:

1
2
3
4
5
6
7
docker run -p 9000:9000 --name minio_office \
-d --restart=always \
-e "MINIO_ACCESS_KEY=admin" \
-e "MINIO_SECRET_KEY=admin123456" \
-v D:\minio:/data \
-v D:\minio\config:/root/.minio \
minio/minio server /data

保证文件的链接是office可访问的,不然会出现下面的提示无法下载文件:

office的相关配置

office相关配置都是在前端中控制的,首先要引入only office的js:

1
<script type="text/javascript" th:src="${docserviceApiUrl}"></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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<script type="text/javascript" th:inline="javascript">
var doca;
var innerAlert = function (message) {
if (console && console.log)
console.log(message);
};
var onReady = function () {
innerAlert("Document editor ready");
};
var onDocumentStateChange = function (event) {
var title = document.title.replace(/\*$/g, "");
document.title = title + (event.data ? "*" : "");
};
var onRequestEditRights = function () {
location.href = location.href.replace(RegExp("mode=view\&?", "i"), "");
};
var onError = function (event) {
if (event)
innerAlert(event.data);
};

var onOutdatedVersion = function (event) {
location.reload(true);
};
const file = /*[[${file}]]*/ {};
console.log("文件信息",file)

var сonnectEditor = function () {
var config={
"document":{
"fileType": file.document.fileType,
"key": file.document.key,
"title": file.document.title,
"url": file.document.url,
"permissions": {
"changeHistory": true,
"comment": true,
"copy": false,
"download": true,
"edit": file.editorConfig.editable,
"fillForms": true,
"modifyContentControl": true,
"modifyFilter": true,
"print": true,
"rename": true,
"review": true
},
},
"documentType": file.documentType,
"editorConfig": {
"callbackUrl": file.editorConfig.callbackUrl,
"customization":{
"forcesave":"true"
},
"user": {
"id": "78ele841",
"name":"John Smith"
},
"lang":"zh-CN",
},
"height": "100%",
"width": "100%"
};
docEditor = new DocsAPI.DocEditor("iframeEditor", config);
};
if (window.addEventListener) {
window.addEventListener("load", сonnectEditor);
} else if (window.attachEvent) {
window.attachEvent("load", сonnectEditor);
}

</script>

文件转换

有些文件不需要经过转换,就能直接预览的,比如pdf文件。但是如果是doc、xls、pptx这种类型的文件,就需要经过only office先转换一次,然后才能编辑。

所以文件上传的时候做一下判断,然后再看是否要传给only office做一次转换,转换完成之后将转换的文件从only office下载回来保存,下次预览就用这个转换后的文件做预览。

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
28
29
30
31
32
33
34
35
36
37
38
@Override
public ResponseResult<ConvertResult> convert(String fileName,String fileUrl) {
String fileExt = fileUtil.getFileExtension(fileName);
FileType fileType = fileUtil.getFileType(fileName);
String internalFileExt = DocumentManager.getInternalExtension(fileType);
if (documentManager.getViewedSuffixes().contains(fileExt)){
ConvertResult convertResult = new ConvertResult();
convertResult.setFileUrl(fileUrl);
convertResult.setFileName(fileName);
convertResult.setProgress(100);
convertResult.setEndConvert(true);
return ResponseResult.ok(convertResult);
}
if (!documentManager.getConvertSuffixes().contains(fileExt)) {
throw new OfficeException("不支持的文件格式: "+ fileExt);
}
log.debug("开始文件转换: {}",fileUrl);
String key = serviceConverter.generateRevisionId(fileUrl);
ConvertResult convertResult = serviceConverter.getConvertedUri(fileUrl, fileExt, internalFileExt, key, true);
if (!convertResult.getEndConvert()) {
//没有转换完成
return ResponseResult.ok(convertResult);
}
//转换完成,可以下载转换后的文件了
String correctName = documentManager.getCorrectName(fileUtil.getFileNameWithoutExtension(fileName) + internalFileExt);
try {
File convertedFile = httpUtil.downloadFile(convertResult.getFileUrl(), documentManager.storagePath(correctName));
log.debug("文件转换成功,上传转换文件...");
OssFileInfo ossFileInfo = ossFileService.uploadFile(convertedFile, CONVERT_PATH,false);
convertedFile.deleteOnExit();
log.debug("文件转换成功: {}",ossFileInfo);
convertResult.setFileUrl(ossFileInfo.getPath());
return ResponseResult.ok(convertResult);
} catch (IOException ex) {
ex.printStackTrace();
throw new OfficeException(ex.getMessage());
}
}

3. 总结

上面也只是涉及了文件编辑相关的,多人协作编辑还没涉及,所以博主也只了解文件编辑相关的原理。各位想深入理解的可以去官网看原文。