目录
- Collabora Online 官网地址: https://www.collaboraoffice.com/
- SDK官网地址: https://sdk.collaboraonline.com/
- 官方文档: https://sdk.collaboraonline.com/
- GitHub: https://github.com/CollaboraOnline/online
- WOPI 官方文档: https://api.onlyoffice.com/zh/editors/wopi/discovery
- 官方 nodejs demo: https://github.com/CollaboraOnline/collabora-online-sdk-examples//tree/master/webapp/nodejs
一、什么是 Collabora Online?
Collabora Onlnien
是一款 开源的 基于 LibreOffice 的云办公套件,它允许用户在浏览器中 创建、编辑和协作处理文档、电子表格、演示文稿和绘图。它是由 Collabora Productivity Ltd 开发的,这家公司是 LibreOffice 社区的主要贡献者之一。
页面如下所示:
点进 Collabora Online 的主页我们可以看到,它的核心产品主要包含如下三类:
- Collabora Online: 是一款基于 Web 的在线办公套件,用户可以通过浏览器访问和使用,不需要在本地计算机上安装任何软件。
- Collabora Office: 是一款桌面版的办公套件,可在 Windows、macOS 和 Linux 上本地安装。
- Collabora Online Development Edition: 缩写为 CODE,直译过来就是 Collabora Online 的 开发版,可以在项目中集成后使用。
这里我们主要使用的就是其中的开发版的
CODE
。在 CODE 中已经实现了很多的成熟的集成方式:
- Alfresco integration:(Java集成) git clone https://github.com/CollaboraOnline/alfresco-collabora-online.git
- EGroupware integration: git clone https://github.com/EGroupware/collabora.git
- Mattermost integration: git clone https://github.com/CollaboraOnline/collabora-mattermost.git
- Collaborative document editing: git clone https://github.com/learnweb/moodle-mod_collabora.git
- Collaborative submissions: git clone https://github.com/CollaboraOnline/collabora-mattermost.git
- Nextcloud integration:(PHP集成) git clone https://github.com/CollaboraOnline/richdocumentscode.git
- ownCloud integration:(PHP集成) git clone https://github.com/CollaboraOnline/richdocumentscode-owncloud.git
(参考:https://sdk.collaboraonline.com/docs/available_integrations.html)
下面是我们使用 SpringBoot 集成 Collabora Online 后的请求过程:
下面我们就来说一下如何下载并启动 CODE 镜像以及如何使用 SpringBoot 集成后实现文件的在线编辑和预览。
二、Docker 下载并启动 CODE
2.1 拉取镜像
首先,使用如下命令拉取镜像:
docker pull collabora/code:latest
拉取过程如下,速度会有些慢:
可以使用阿里的镜像源,加快拉取速度:
{
"registry-mirrors":["https://6kx4zyno.mirror.aliyuncs.com"]}
拉取完毕之后,镜像如下所示:
2.2 启动镜像
镜像拉取完毕之后,使用如下命令启动镜像即可:
docker run \--name"collabora_code"\-t\-d\-p0.0.0.0:9980:9980 \-e"username=admin"\-e"password=Pwd4C0De"\
--cap-add MKNOD \--privileged\-v /etc/collabora/coolwsd.xml:/etc/coolwsd/coolwsd.xml \
collabora/code:latest
补充:
--privileged
是让当前容器的用户即使不是 root,也可以执行任何需要root权限的操作。
coolwsd.xml
是 Collabora Online 的配置文件,它控制着 Collabora Online 服务中 是否启用 TSL/SSL 加密、字体资源位置、对特定 IP 或域名的访问控制 等各种参数的设置。
coolwsd
:Collabora Online Web Service Daemon 的缩写,它是一个守护进程,运行在服务器上,是 Collabora Online 办公套件的核心组件之一。coolwsd 主要负责提供后台服务,是的用户能够 通过 Web 浏览器访问和使用 LibreOffice 功能,从而实现在线文档编辑和写作。
CODE 镜像 docker run 常用挂载项:
- 指定 coolwsd.xml 配置文件,里面包含了 CODE 的最最核心配置:**-v /yourPath/coolwsd.xml:/etc/coolwsd/coolwsd.xml**
2.3 访问界面
注意:
- 在线编辑地址后面的
WOPISrc
要拼接 WOPI 服务地址的,所以目前还不能访问。可以先访问下管理系统地址。 - 通过cool.html访问时,后面传输的任何参数,例如
access_token
都会被转发到对应的 WOPI 服务的请求上,用于鉴权等操作。
访问地址后,界面如下所示:
2.4 补充:nextcloud 的镜像启动
nextcloud
使用了 PHP 语言集成了 Collabora Online 的一套成熟的系统。
- 官网地址: https://nextcloud.com/
首先使用如下命令拉取镜像:
docker pull nextcloud:latest
使用如下命令启动镜像:
docker run \--name nextcloud_test \-d\--restart=always \-p8880:80 \
- 访问地址: http://localhost:8880/
- 账号/密码: admin/Pwd4C0De(由 CODE 启动时指定)
三、SpringBoot 实现 WOPI 服务
3.1 什么是WOPI?
WOPI
:全称为 Web Office Protocol Interface,是一种由微软提出的协议,旨在允许 Web 应用程序能够无缝地与 Office 文档进行交互。WOPI 主要应用于在线编辑和预览 Microsoft Office 文档的场景,使得用户可以在 Web应用中直接打开、编辑和保存 Office 文档,而无需下载文档到本地或安装 Office 套件。
其实 WOPI 协议就是一系列规定了接口地址、入参/出参的 RESTful 接口规范。核心的接口主要有3个:
- 获取文件信息接口:/wopi/file/{id},Get请求这个接口可以 获取文件的名称、大小、是否可编辑等信息。(参考:https://api.onlyoffice.com/zh/editors/wopi/restapi/checkfileinfo)
- 获取文件内容接口:/files/{id}/contents,GET请求这个接口可以 读取文件的信息,以字节流的形式返回。(参考:https://api.onlyoffice.com/zh/editors/wopi/restapi/getfile)
- 写入文件内容接口:/files/{id}/contents,POST请求这个接口用于 写入文件内容,即保存编辑后的内容,将文件流写入文件。(参考:https://api.onlyoffice.com/zh/editors/wopi/restapi/putfile)
3.2 Spring Boot 简单实现
WopiController.java
(接口类,用于校验权限、操作文件)
importcom.demo.common.model.auth.details.LoginUserUtil;importcom.demo.common.web.Result;importcom.demo.online.vo.WopiFileInfoVO;importio.swagger.annotations.ApiOperation;importlombok.extern.slf4j.Slf4j;importorg.springframework.web.bind.annotation.*;importjavax.annotation.Resource;importjavax.servlet.ServletInputStream;importjavax.servlet.http.HttpServletRequest;importjava.io.IOException;importjava.nio.file.Paths;importjava.nio.file.StandardCopyOption;importjava.util.Objects;/**
* <p> @Title WopiController
* <p> @Description WOPI在线编辑Controller
*
* @author ACGkaka
*/@Slf4j@RestController@RequestMapping("/wopi")publicclassWopiController{
@ResourceprivateFileInfoService fileInfoService;@ApiOperation("在线编辑-根据ID查询文件信息")@GetMapping("/files/{id}")publicWopiFileInfoVOfindWopiFileInfoById(@PathVariable("id")String id){
// 查询信息FileInfo file = fileInfoService.getAndCheckPermissionById(id);Integer fileSize = file.getSize();// 记录日志LoginUser loginUser =LoginUserUtil.getLoginUser();
log.info(">>>>>>>>>>【INFO】WOPI文件信息,uid:{},userName:{},eid:{},orgName:{},fileInfoId:{},fileInfoName:{},editFlag:{}",
loginUser.getId(), loginUser.getUsername(), loginUser.getEid(), loginUser.getOrgName(), id, fileInfo.getFileInfoName(), fileInfo.getEditFlag());// 封装返回returnnewWopiFileInfoVO(fileInfo.getFileInfoName(), fileSize,Objects.equals(fileInfo.getEditFlag(),"1"));}@ApiOperation("在线编辑-根据ID查询文件内容")@GetMapping("/files/{id}/contents")publicbyte[]findWopiFileContentsById(@PathVariable("id")String id){
fileInfo file = fileInfoService.getAndCheckPermissionById(id);return fileInfoService.getBytes(file.getId());}@ApiOperation("在线编辑-根据ID查询文件内容")@PostMapping("/files/{id}/contents")publicResult<Object>saveWopiFileContentsById(@PathVariable("id")String id,HttpServletRequest request){
fileInfo fileInfo = fileInfoService.getAndCheckPermissionById(id);if(!Objects.equals(fileInfo.getEditFlag(),"1")){
LoginUser loginUser =SysUserUtil.getloginUser();
log.error(">>>>>>>>>>【ERROR】暂无权限在线编辑该文件,请确认后重试,fileInfo.getEditFlag():{},fileInfo.getId():{},uid:{}",
fileInfo.getEditFlag(), fileInfo.getId(), loginUser.getId());thrownewIllegalArgumentException("暂无权限在线编辑该文件,请确认后重试");}FileInfo fileInfo = fileInfoService.getFileInfo(fileInfo.getFileInfoId());if(fileInfo ==null){
log.error(">>>>>>>>>>【ERROR】在线文件信息未找到,请刷新后重试,fileInfoId:{}", fileInfo.getFileInfoId());thrownewIllegalArgumentException("在线文件信息未找到,请刷新后重试");}writeToFile(Paths.get(fileInfo.getCatalogue()+ fileInfo.getPath()), request);returnResult.succeed();}/**
* 将 request 内容写入文件
*/privatevoidwriteToFile(java.nio.file.Path path,HttpServletRequest request){
ServletInputStream inputstream;try{
inputstream = request.getInputStream();java.nio.file.Files.copy(inputstream, path,StandardCopyOption.REPLACE_EXISTING);}catch(IOException e){
log.error(">>>>>>>>>>【ERROR】在线文件写入失败,请刷新后重试,原因:{}", e.getMessage(), e);thrownewIllegalArgumentException("在线文件写入失败,请刷新后重试");}}}
WopiFileInfoVO.java
(实体类,用于封装文件信息)
importcom.demo.common.model.auth.details.LoginUserUtil;importcom.alibaba.fastjson.JSONObject;importcom.fasterxml.jackson.annotation.JsonProperty;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;importlombok.extern.slf4j.Slf4j;/**
* <p> @Title WopiFilesByIdVO
* <p> @Description 文件信息封装(参考文档:https://api.onlyoffice.com/zh/editors/wopi/restapi/checkfileinfo)
*
* @author ACGkaka
* @date 2024/5/28 19:34
*/@Slf4j@Data@NoArgsConstructor@AllArgsConstructorpublicclassWopiFileInfoVO{
/**
* 文件名称(例:test.docx)
*/@JsonProperty("BaseFileName")privateStringBaseFileName;/**
* 版本(每次编辑和保存文档时,都必须更改版本。 给定文件的版本号不得重复。例:Khirz6zTPdfd7)
*/@JsonProperty("Version")privateStringVersion="Version";/**
* 面包屑导航——客户端向用户显示的名称,用于指示 WOPI 服务器的品牌名称。
*/@JsonProperty("BreadcrumbBrandName")privateStringBreadcrumbBrandName="5G随e签";/**
* 面包屑导航——当用户单击显示 BreadcrumbBrandName的 UI 时,WOPI 客户端导航到的网页的 URL。
*/@JsonProperty("BreadcrumbBrandUrl")privateStringBreadcrumbBrandUrl="https://esign.it.10086.cn/saas/#/login";// /**// * 面包屑导航——WOPI 客户端向用户显示的文件名。如果未指定此参数,则使用 BaseFileName 参数。// */// @JsonProperty("BreadcrumbDocName")// private String BreadcrumbDocName = "BreadcrumbDocName";// /**// * 面包屑导航——WOPI 客户端向用户显示的名称,指示文件所在的文件夹的名称。// */// @JsonProperty("BreadcrumbFolderName")// private String BreadcrumbFolderName = "BreadcrumbFolderName";// /**// * 面包屑导航——当用户单击显示 BreadcrumbFolderName的 UI 时,WOPI 客户端导航到的网页的 URL。// */// @JsonProperty("BreadcrumbFolderUrl")// private String BreadcrumbFolderUrl = "BreadcrumbFolderUrl";/**
* 文件大小(单位KB)
*/@JsonProperty
版权归原作者 不愿放下技术的小赵 所有, 如有侵权,请联系我们删除。