SpringBoot中整合ONLYOFFICE在线编辑

📅 发布时间:2026/7/8 7:14:48 👁️ 浏览次数:
SpringBoot中整合ONLYOFFICE在线编辑
SpringBoot整合OnlyOfficeSpringBoot整合OnlyOffice实现在线编辑1. 搭建私有的OnlyOffice的服务2. SpringBoot进行交互2.1 环境2.2 我们的流程2.3 接口规划2.3.1 获取编辑器配置的接口2.3.2 文件下载地址2.3.3 文件下载地址3. 总结4. 注意4.1 你的项目的地址一定一定要和onlyoffice可以正常通讯如果不行则一直不可能成功。4.2 TOKEN是可以可选项建议一开始不要使用后面有需要的时候再去添加。4.3 一定要看一下官网文档文档真的很全很重要4.4 协同的话只要参数就是一个KEY如果需要超过20个的限制直接重新编译即可大神一大堆很容易就可以找到。SpringBoot整合OnlyOffice实现在线编辑公司有一个需求就是实现 *Word* , *Excel* ,等文件的在线编辑市场上面进行了多方面的选型考虑了 *[OpenOffice](https://openoffice.apache.org/)* , *[Office Online](https://www.microsoft.com/zh-cn/microsoft-365/free-office-online-for-the-web?legRedirtrueCorrelationId13c8a865-b9b0-48ff-b3ed-3ea9ec31cd55)*, 但是最终还是选择了 *[OnlyOffice](https://www.onlyoffice.com/zh/)* 这个产品。 他的一个很大的优势在于开源支持协同社区比较活跃。api比较全面还有中文的文档。还有一点比较好的就是支持协同并且支持协同虽然协同在社区版中存在限制但是支持代码修改可以重新编译。社区的大佬很多很赞。唯一遗憾的就是效率比较低在使用私有对象存储的时候存在延迟。其他的没有使用到所以不进行评论。中文文档[https://api.onlyoffice.com/zh/editors/basic](https://api.onlyoffice.com/zh/editors/basic)1. 搭建私有的OnlyOffice的服务搭建过程这里就不进行涉猎了建议使用docker进行搭建下载官方镜像包即可,现在dockerhub被墙自行解决不建议自己再次打包因为我在尝试的时候总是出现莫名奇妙的问题可能是我的问题。推荐使用官网原版镜像。根据官方文档一步步操作即可。搭建过程中如果是自己玩建议不要开启 **JWT** 生产环境建议开一下。但是开的成本就是你对接的时候需要获取token然后在进行交互。2. SpringBoot进行交互2.1 环境java 17boot: 3.0.5页面一个h5页面即可需要的其他依赖!-- ... 其他的依赖自行添加即可不重要比如 fastjson2,jackson 等 -- !-- 这个JAR 主要的作用是与OnlyOffice交互的时候生成token使用的 -- dependency groupIdcom.inversoft/groupId artifactIdprime-jwt/artifactId version1.3.1/version /dependency2.2 我们的流程我们使用一个 H5 页面即可页面通过加载一个 app.js 。然后通过一个 config 进行渲染就可以实现一个编辑。app.js 是核心js文件only office 我只使用他的一个编辑的功能这是一个核心就是编辑文件文件的来源和存储与它无关被编辑的文件从哪里获取从 config 对象中的配置获取这里就需要自行实现。编辑后的文件如何获取config对象中有一个回调地址这个地址会给到服务器一个编辑的状态并且携带一个获取编辑后文件的url(这个url就是only office 服务中的一个文件下载地址)根据这个url来获取编辑后的文件。然后在对这个文件进行存储。回调的实现参考https://api.onlyoffice.com/zh/editors/callback#status2.3 接口规划一共设计三个接口获取编辑器的配置获取需要编辑的文件流编辑后保存文件的回调保存后的文件注意这里编辑后的文件并不是在回调里面以流的形式给而是在回调接口里面给服务器一个状态根据状态去获取一个下载编辑后文件的一个地址然后根据地址去主动的获取文件。2.3.1 获取编辑器配置的接口/** * 被编辑文件的下载连接 * 这里就是自己服务的配置地址 * only office 调用你的服务的地址一定是 onlyoffice服务可以ping通的你的项目地址。ping不通白搭 */ Value(${only.office.downUrl}) private String downFileUrl ; /** * 这里是回调地址例如 http://192.168.0.10:8080/office/edit/callback/{fileId} * 自行定义即可就是后面自己编写的接口但是一定要通可onlyoffice服务互通 * only office 调用你的服务的地址一定是 onlyoffice服务可以ping通的你的项目地址。ping不通白搭 */ Value(${only.office.callBackUrl}) private String editCallBackUrl ; Operation(summary 根据文件的ID来获取在线编辑的配置和token) PostMapping(/token/{fileId}) Parameters({ Parameter(name fileId, description 不是对象ID是文件的ID, in ParameterIn.PATH) }) public ResultVo? getToken(PathVariable String fileId) { String fileKey ; if (redisUtil.hHasKey(RedisName.ONLY_OFFICE_FILE_KYE,fileId)) { fileKey redisUtil.hget(RedisName.ONLY_OFFICE_FILE_KYE,fileId).toString(); //return ResultVo.error(CustomExceptionType.ONLY_OFFICE_COORDINATION_ERROR); }else{ fileKey fileId RandomUtil.randomNumbers(10); } String json { document: { title: %s, key: %s, fileType:%s, lang: zh-CN, permissions: { comment: true, commentGroups: { edit: [Group2, Group1], remove: [], view: }, copy: true, deleteCommentAuthorOnly: false, download: true, edit: true, editCommentAuthorOnly: false, fillForms: true, modifyContentControl: true, modifyFilter: true, print: true, review: true, reviewGroups: [Group1, Group2, ] }, url: %s }, editorConfig: { customization:{ autosave: true, forcesave: true } lang: zh-CN, callbackUrl: %s, onEditing: { mode: fast, change: true }, mode: edit, user: { group: Group1, id: %s, name: %s } } } ; // TODO 这里文件的key可以通过redis进行保存这样可以支持多人在线协同现在不做处理 json String.format(json, fileInfo.getFileName(), fileKey, // TODO 这里是文件类型自行定义 xlsx, // TODO 这里是文件下载地址fileId 为文件的唯一标识自行定义 downFileUrl fileId, // TODO 这里是定义回调地址fileId 为文件的唯一标识用来区分是那个文件编辑的回调。 editCallBackUrl fileId, userid,username); MapString, Object map JSONObject.parseObject(json, new TypeTokenMapString, Object() { }.getType()); // TODO 这里是获取onlyoffice 交互的token,自己写的建议直接注释 // String token jwtManager.createToken(map); // map.put(token, token); // TODO 这个key可以直接注释这里主要作用是协同 redisUtil.hset(RedisName.ONLY_OFFICE_FILE_KYE,fileId,fileKey,60*60*24); return ResultVo.success(map); }2.3.2 文件下载地址这个接口的作用就是获取一个文件流根据ID来获取一个文件流这里的地址就是上一个接口中下载文件的地址。GetMapping(down/file/{fileId}) Operation(summary 根据参数下载一个文件) public void downFolderById(PathVariable String fileId, HttpServletResponse response){ // TODO 1. 根据文件的唯一ID来获取数据库中的记录 EtmfFileInfo fileInfo fileInfoOpt.getById(fileId); // TODO 2. 根据下载路径从 minio 中获取文件流 因为我们使用的是minio其他的自行切换即可 try (InputStream inputStream smoMinIoUtils.downloadFile(fileInfo.getFileUrl())) { downFileInfo(response, fileInfo, inputStream); } catch (ServerException | ErrorResponseException | InsufficientDataException | IOException | NoSuchAlgorithmException | InvalidKeyException | InvalidResponseException | XmlParserException | InternalException e) { JwtUtil.responseError(response, 500L, 文件下载失败 e.getMessage()); } } public static void downFileInfo(HttpServletResponse response, EtmfFileInfo fileInfo, InputStream inputStream) throws IOException { response.setCharacterEncoding(UTF-8); response.setContentType(application/octet-stream; charsetUTF-8); response.addHeader(Content-Disposition, attachment;filename URLEncoder.encode(fileInfo.getFileName(), StandardCharsets.UTF_8)); ServletOutputStream stream response.getOutputStream(); IOUtils.copy(inputStream,stream); stream.flush(); stream.close(); }2.3.3 文件下载地址这里是文件的回调地址主要就是获取一个状态码然后根据状态码判定是否保存文件。Operation(summary 文件编辑之后的回调) Parameters({ Parameter(name fileId, description 文件的ID, in ParameterIn.PATH) }) PostMapping(/edit/callback/{fileId}) public void editCallBack(PathVariable String fileId, HttpServletRequest request, HttpServletResponse response) { try { PrintWriter writer response.getWriter(); String body; try { Scanner scanner new Scanner(request.getInputStream()); scanner.useDelimiter(\A); body scanner.hasNext() ? scanner.next() : ; scanner.close(); } catch (Exception ex) { writer.write(get request.getInputStream error: ex.getMessage()); return; } if (body.isEmpty()) { writer.write(empty request.getInputStream); return; } JSONObject jsonObj JSON.parseObject(body); int status (Integer) jsonObj.get(status); log.debug(文件编辑获取到的参数是{}, JSON.toJSONString(jsonObj)); int saved 0; if (List.of(2,3,6).contains(status)) { String downloadUri (String) jsonObj.get(url); log.debug(文件进行保存处理需要保存的状态值是{}可以获取到文件的路径是{}, status,downloadUri); try { URL url new URL(downloadUri); // 根据文件下载地址来获取编辑后的文件流 HttpURLConnection connection (HttpURLConnection) url.openConnection(); InputStream stream connection.getInputStream(); if (stream null) { throw new Exception(Stream is null); } // TODO 根据文件的唯一标识获取数据库中文件记录 EtmfFileInfo fileInfo fileInfoOpt.getById(fileId); // TODO 根据文件流创建一个文件 File savedFile new File(fileInfo.getFileName()); try (FileOutputStream out new FileOutputStream(savedFile)) { int read; final byte[] bytes new byte[1024]; while ((read stream.read(bytes)) ! -1) { out.write(bytes, 0, read); } out.flush(); } // TODO 根据文件上传到 MINIO中 boolean b smoMinIoUtils.uploadFile(fileInfo.getFileUrl(), savedFile); log.info(编辑文件后文件上传状态{}上传的文件是{}Id是{},b,fileInfo.getFileName(),fileId); savedFile.delete(); connection.disconnect(); } catch (Exception ex) { saved 1; ex.printStackTrace(); }finally { // 正常保存的时候剔除掉redis缓存 if (status TWO) { redisUtil.hdel(RedisName.ONLY_OFFICE_FILE_KYE,fileId); } } } writer.write({error: saved }); writer.flush(); writer.close(); log.debug(编辑完成--------------返回值是{},{error: saved }); } catch (IOException e) { e.printStackTrace(); throw new SmoGlobalException(CustomExceptionType.OTHER_ERROR); } }3. 总结文件的在线编辑主要就是依托与onlyoffice实现的而编辑器的配置是通过我们的接口来定义的接口中的配置可以自由的定义编辑器的文件类型窗口大小文件来源回调地址保存类型等等。你需要编辑的文件可以放在任意的位置只要你的接口可以通过流的方式给到onlyofiice编辑器即可。文件编辑后的处理都是在回调中处理的最好先看一下文档的回调写法。回调的时候记得打印日志观察一下接口的内容一定要记得是通过回调中的url参数来获取编辑后的文件流的并不是通过回调接口直接把文件流给到你。我在这里没有注意看饶了弯路。所以提醒一下。4. 注意4.1 你的项目的地址一定一定要和onlyoffice可以正常通讯如果不行则一直不可能成功。4.2 TOKEN是可以可选项建议一开始不要使用后面有需要的时候再去添加。4.3 一定要看一下官网文档文档真的很全很重要4.4 协同的话只要参数就是一个KEY如果需要超过20个的限制直接重新编译即可大神一大堆很容易就可以找到。