SpringBoot中整合ONLYOFFICE在線編輯

SpringBoot整合OnlyOffice

  • SpringBoot整合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?legRedir=true&CorrelationId=13c8a865-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: 17
boot: 3.0.5
頁面:一個h5頁面即可
需要的其他依賴

<!-- ... 其他的依賴自行添加即可,不重要,比如 fastjson2,jackson 等 --><!-- 這個JAR 主要的作用是與OnlyOffice交互的時候生成token使用的 -->
<dependency><groupId>com.inversoft</groupId><artifactId>prime-jwt</artifactId><version>1.3.1</version></dependency>

2.2 我們的流程

我們使用一個 H5 頁面即可,頁面通過加載一個 app.js 。然后通過一個 config 進行渲染,就可以實現一個編輯。app.js 是核心js文件

  1. only office 我只使用他的一個編輯的功能(這是一個核心,就是編輯文件,文件的來源和存儲與它無關)
  2. 被編輯的文件從哪里獲取?從 config 對象中的配置獲取,這里就需要自行實現。
  3. 編輯后的文件如何獲取?config對象中有一個回調地址,這個地址會給到服務器一個編輯的狀態,并且攜帶一個獲取編輯后文件的url(這個url就是only office 服務中的一個文件下載地址),根據這個url來獲取編輯后的文件。然后在對這個文件進行存儲。
    回調的實現參考:https://api.onlyoffice.com/zh/editors/callback#status

在這里插入圖片描述

2.3 接口規劃

一共設計三個接口,

  1. 獲取編輯器的配置
  2. 獲取需要編輯的文件流
  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");Map<String, Object> map = JSONObject.parseObject(json, new TypeToken<Map<String, 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; charset=UTF-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個的限制直接重新編譯即可,大神一大堆,很容易就可以找到。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/39063.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/39063.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/39063.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Java中的單元測試與集成測試最佳實踐

Java中的單元測試與集成測試最佳實踐 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將深入探討Java中的單元測試與集成測試最佳實踐。在軟件開發中&am…

三層交換基礎

一、什么是三層交換 三層交換是一種在OSI模型第三層&#xff0c;即網絡層上工作的網絡設備技術&#xff0c;它整合了二層交換機的功能和路由器的部分功能&#xff0c;以實現更高效的網絡數據轉發和路由選擇。三層交換技術的核心在于結合了二層交換技術和三層轉發技術&#xff…

【RabbitMQ實戰】Springboot 整合RabbitMQ組件,多種編碼示例,帶你實踐 看完這一篇就夠了

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、對RabbitMQ管理界面深入了解1、在這個界面里面我們可以做些什么&#xff1f; 二、編碼練習&#xff08;1&#xff09;使用direct exchange(直連型交換機)&a…

2024 年的 13 個 AI 趨勢

2024 年的 13 個 AI 趨勢 人工智能對環境的影響和平人工智能人工智能支持的問題解決和決策針對人工智能公司的訴訟2024 年美國總統大選與人工智能威脅人工智能、網絡犯罪和社會工程威脅人工智能治療孤獨與對人工智能的情感依賴人工智能影響者中國爭奪人工智能霸主地位人工智能…

Java中的機器學習模型集成與訓練

Java中的機器學習模型集成與訓練 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將探討在Java中如何進行機器學習模型的集成與訓練。隨著人工智能和機器…

【Lua小知識】Vscode中Emmylua插件大量報錯的解決方法

起因 Vscode寫Lua用的好好的&#xff0c;最近突然出現了大量報錯。 看報錯是有未定義的全局變量&#xff0c;這里查日志才發現是由于0.7.5版本新增診斷啟用配置&#xff0c;所以導致了原先好的代碼&#xff0c;現在出現了大量的報錯。 解決方案一 最直接的方法當然是在配置中直…

用攝像頭實現識別道路中的車道線、行人與車輛檢測(級聯分類器、HOG+SVM、行人檢測)

基于樹莓派的智能小車&#xff0c;用攝像頭實現識別道路中的車道線識別、行人檢測與車輛檢測。 本項目旨在開發一套基于攝像頭的智能道路環境感知系統&#xff0c;該系統能夠實時識別道路中的車道線、行人與車輛&#xff0c;為自動駕駛汽車、智能交通管理以及輔助駕駛系統提供關…

LeetCode熱題100刷題3:3. 無重復字符的最長子串、438. 找到字符串中所有字母異位詞、560. 和為 K 的子數組

3. 無重復字符的最長子串 滑動窗口、雙指針 class Solution { public:int lengthOfLongestSubstring(string s) {//滑動窗口試一下//英文字母、數字、符號、空格,ascii 一共包含128個字符vector<int> pos(128,-1);int ans 0;for(int i0,j0 ; i<s.size();i) {//s[i]…

python 中的生成器

目錄 生成器示例基本生成器示例無限序列生成器使用生成器表達式實用示例&#xff1a;按行讀取大文件生成器的 send、throw 和 close 方法 生成器和迭代器迭代器&#xff08;Iterator&#xff09;定義創建使用示例 生成器&#xff08;Generator&#xff09;定義創建使用示例 主要…

【python學習】自定義函數的一些高級用法-2

8. 生成器函數 生成器函數允許你定義一個可以“記住”其當前執行狀態的函數&#xff0c;并在下次調用時從上次離開的位置繼續執行。生成器函數使用yield關鍵字而不是return。 def simple_generator(): yield 1 yield 2 yield 3 gen simple_generator() print(next(gen)) # …

隱私計算實訓營第二期第十課:基于SPU機器學習建模實踐

隱私計算實訓營第二期-第十課 第十課&#xff1a;基于SPU機器學習建模實踐1 隱私保護機器學習背景1.1 機器學習中隱私保護的需求1.2 PPML提供的技術解決方案 2 SPU架構2.1 SPU前端2.2 SPU編譯器2.3 SPU運行時2.4 SPU目標 3 密態訓練與推理3.1 四個基本問題3.2 解決數據來源問題…

全新升級!中央集中式架構功能測試為新車型保駕護航

“軟件定義汽車”新時代下&#xff0c;整車電氣電氣架構向中央-區域集中式發展已成為行業共識&#xff0c;車型架構的變革帶來更復雜的整車功能定義、更多的新技術的應用&#xff08;如SOA服務化、智能配電等&#xff09;和更短的車型研發周期&#xff0c;對整車和新產品研發的…

OkHttp的源碼解讀1

介紹 OkHttp 是 Square 公司開源的一款高效的 HTTP 客戶端&#xff0c;用于與服務器進行 HTTP 請求和響應。它具有高效的連接池、透明的 GZIP 壓縮和響應緩存等功能&#xff0c;是 Android 開發中廣泛使用的網絡庫。 本文將詳細解讀 OkHttp 的源碼&#xff0c;包括其主要組件…

Qt實現手動切換多種布局

引言 之前寫了一個手動切換多個布局的程序&#xff0c;下面來記錄一下。 程序運行效果如下&#xff1a; 示例 需求 通過點擊程序界面上不同的布局按鈕&#xff0c;使主工作區呈現出不同的頁面布局&#xff0c;多個布局之間可以通過點擊不同布局按鈕切換。支持的最多的窗口…

如何使用 AppML

如何使用 AppML AppML(Application Markup Language)是一種輕量級的標記語言,旨在簡化Web應用的創建和部署過程。它允許開發者通過XML或JSON格式的配置文件來定義應用的結構和行為,從而實現快速開發和靈活擴展。AppML特別適用于構建數據驅動的企業級應用,它可以與各種后端…

pytorch跑手寫體實驗

目錄 1、環境條件 2、代碼實現 3、總結 1、環境條件 pycharm編譯器pytorch依賴matplotlib依賴numpy依賴等等 2、代碼實現 import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms import matpl…

burpsuite 設置監聽窗口 火狐利用插件快速切換代理狀態

一、修改burpsuite監聽端口 1、首先打開burpsuite&#xff0c;點擊Proxy下的Options選項&#xff1a; 2、可以看到默認的監聽端口為8080&#xff0c;首先選中我們想要修改的監聽&#xff0c;點擊Edit進行編輯 3、將端口改為9876&#xff0c;并保存 4、可以看到監聽端口修改成功…

typescript學習回顧(五)

今天來分享一下ts的泛型&#xff0c;最后來做一個練習 泛型 有時候&#xff0c;我們在書寫某些函數的時候&#xff0c;會丟失一些類型信息&#xff0c;比如我下面有一個例子&#xff0c;我想提取一個數組的某個索引之前的所有數據 function getArraySomeData(newArr, n:numb…

JVM原理(十):JVM虛擬機調優分析與實戰

1. 大內存硬件上的程序部署策略 這是筆者很久之前處理過的一個案例&#xff0c;但今天仍然具有代表性。一個15萬PV/日左右的在線文檔類型網站最近更換了硬件系統&#xff0c;服務器的硬件為四路志強處理器、16GB物理內存&#xff0c;操作系統為64位CentOS5.4&#xff0c;Resin…

js數組方法歸納——concat、join、reverse

1、concat( ) 用途&#xff1a;可以連接兩個或多個數組&#xff0c;并將新的數組返回該方法不會對原數組產生影響 var arr ["孫悟空","豬八戒","沙和尚"];var arr2 ["白骨精","玉兔精","蜘蛛精"];var arr3 [&…