项目地址
快速使用
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
   | oo:   secret: mnu8wQaoxveEtDY6           timeout: 300000   doc-service: http://172.31.240.1:8886    call-back-url: /onlyOffice/save     download-file: http://172.31.240.1:9090/download/    localhost-address: http://172.31.240.1:9090    hist-num:     max-size:     cache:        document:     permissions:           edit:         chat: false                comment:              copy:                 deleteCommentAuthorOnly:          download:                  editCommentAuthorOnly:          fillForms:                 modifyContentControl:          modifyFilter:                    print:                        protect:                      review: false                    view:         chat: false                comment:              copy:                 deleteCommentAuthorOnly:          download:                  editCommentAuthorOnly:          fillForms:                 modifyContentControl:          modifyFilter:                    print:                        protect:                      review:  false               editor:     customization:             anonymous:           request: false          label:           goback:              blank: false            requestClose:           text:                   url:                  logo:                 image:                imageDark:            url:                review:         hideReviewDisplay:              hoverMode:                    feedback:                      visible: false         url:       autosave:                forcesave:                 comments: false               compactHeader:           compactToolbar:          compatibleFeatures:         features:                    help: false                      hideNotes: true                 hideRightMenu: true             hideRulers:                 macros: false                    macrosMode:                 mentionShare:               plugins:                    toolbarHideFileName:         toolbarNoTabs:                uiTheme:                      unit:                         zoom:                      plugins:                   autostart:                 -       pluginsData:               -
    convert:       async:       codePage:       delimiter:       region:          documentLayout:       drawPlaceHolders:       drawFormHighlight:       isPrint:     documentRenderer:       textAssociation:     spreadsheetLayout:       fitToHeight:                fitToWidth:                 gridLines:                  headings:                   ignorePrintArea:            orientation:                scale:       margins:          bottom:         left:         right:         top:       pageSize:          height:         width:     thumbnail:         aspect:          first:           height:          width:    
 
   | 
核心接口
以下所有的方法都在OnlyServiceAPI类中。
openDocument()
打开文件时,请求接口地址。
- 用户使用文档管理器(在其浏览器中找到)打开文档以供查看或编辑。
 - 浏览器文档管理器从文档存储服务接收用户可用的所有文档的列表。
 - 文档标识符及其在文档存储服务中的链接使用JavaScript API发送到文档编辑器。
 - 该文档编辑器构成的一个请求文档编辑服务的打开文档。在文档编辑器使用文档标识符和从所接收的它的链路文档管理器(步骤2)。
 - 该文档编辑服务下载从文档文件的文件存储服务使用提供的ID和链接。在此步骤中,还为文档编辑器执行文件到Office Open XML格式的转换,以获得更好的性能和格式兼容性。
 - 准备好后,文档编辑服务将文档文件传输到基于浏览器的文档编辑器。
 - 文档编辑器显示文档文件和/或(如果提供了适当的权限)允许其编辑。
 

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
   |  @RequestMapping("/onlyOffice/{mode}/{id}") public ModelAndView openDocument(@PathVariable String mode,@PathVariable String id, Model model) {     log.info("only office key:" + id);     OnFile onFile = onFileService.getById(id);
 
      
 
      FileUser user = new FileUser();     user.setId(TempUser.getUserId());     user.setName(TempUser.getUserName());     SecurityUtils.setUserSession(user);     
 
      Map<String,Object> map = new HashMap<>();     map.put("fileId",onFile.getFileId());     map.put("fileName",onFile.getFileName());     map.put("fileType",onFile.getFileType());     map.put("fileSize",onFile.getFileSize());     map.put("version",onFile.getVersion());
           Map config = onlyServiceAPI.openDocument(map, mode, false);
           SecurityUtils.removeUserSession();
      String s = JSON.toJSONString(config);     log.info("only office config:" + s);     model.addAllAttributes(config);     return new ModelAndView("onlyOffice"); }
 
 
  | 
接口返回配置信息,页面通过onlyoffice提供的API渲染编辑器。 new DocsAPI.DocEditor(#xx,config );
返回信息中的docServiceApiUrl 是onlyoffice的API地址
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
   | { "editorConfig": {     "mode": "edit",     "customization": {         "feedback": {             "visible": false         },         "help": false,         "goback": {             "blank": false         },         "macros": false,         "autosave": false,         "comments": false,         "review": {},         "hideRightMenu": true,         "anonymous": {             "request": false         },         "forcesave": true,         "logo": {},         "hideNotes": true     },     "plugins": {         "pluginsData": [],         "autostart": []     },     "callbackUrl": "http://172.31.240.1:9090/onlyOffice/save",     "lang": "zh-CN",     "user": {         "name": "TongHuic7bba5",         "id": "c7bba5"     }   }, "docServiceApiUrl": "http://172.31.240.1:8886/web-apps/apps/api/documents/api.js", "documentType": "word", "document": {     "permissions": {         "edit": true,         "chat": false,         "review": false     },     "title": "fdfs.docx",     "fileType": "docx",     "key": "63f560ec03a94654b10cd4fdeebec05a",     "url": "http://172.31.240.1:9090/download/09cee8767dd3476280fa865bacfaf213",     "info": {         "sharingSettings": [{             "isLink": true,             "permissions": ["Full Access"],             "user": "TongHuic7bba5"         }],         "created": "2023-08-05 21:38:25"     } }, "type": "desktop", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlZGl0b3JDb25maWciOnsiY2FsbGJhY2tVcmwiOiJodHRwOi8vMTcyLjMxLjI0MC4xOjkwOTAvb25seU9mZmljZS9zYXZlIiwibGFuZyI6InpoLUNOIiwibW9kZSI6ImVkaXQiLCJ1c2VyIjp7ImlkIjoiYzdiYmE1IiwibmFtZSI6IlRvbmdIdWljN2JiYTUifSwiY3VzdG9taXphdGlvbiI6eyJhbm9ueW1vdXMiOnsicmVxdWVzdCI6ZmFsc2V9LCJhdXRvc2F2ZSI6ZmFsc2UsImNvbW1lbnRzIjpmYWxzZSwiZmVlZGJhY2siOnsidmlzaWJsZSI6ZmFsc2V9LCJmb3JjZXNhdmUiOnRydWUsImdvYmFjayI6eyJibGFuayI6ZmFsc2V9LCJoZWxwIjpmYWxzZSwiaGlkZU5vdGVzIjp0cnVlLCJoaWRlUmlnaHRNZW51Ijp0cnVlLCJsb2dvIjp7fSwibWFjcm9zIjpmYWxzZSwicmV2aWV3Ijp7fX0sInBsdWdpbnMiOnt9fSwiZG9jdW1lbnRUeXBlIjoid29yZCIsImRvY3VtZW50Ijp7ImZpbGVUeXBlIjoiZG9jeCIsInRpdGxlIjoiZmRmcy5kb2N4IiwidXJsIjoiaHR0cDovLzE3Mi4zMS4yNDAuMTo5MDkwL2Rvd25sb2FkLzA5Y2VlODc2N2RkMzQ3NjI4MGZhODY1YmFjZmFmMjEzIiwia2V5IjoiNjNmNTYwZWMwM2E5NDY1NGIxMGNkNGZkZWViZWMwNWEiLCJpbmZvIjp7ImNyZWF0ZWQiOiIyMDIzLTA4LTA1IDIxOjM4OjI1Iiwic2hhcmluZ1NldHRpbmdzIjpbeyJpc0xpbmsiOnRydWUsInBlcm1pc3Npb25zIjpbIkZ1bGwgQWNjZXNzIl0sInVzZXIiOiJUb25nSHVpYzdiYmE1In1dfSwicGVybWlzc2lvbnMiOnsiY2hhdCI6ZmFsc2UsImVkaXQiOnRydWUsInJldmlldyI6ZmFsc2V9fSwidHlwZSI6ImRlc2t0b3AifQ.HA6qS3P-czSDuScxH55tq5Bep1aN72N_F5YbXlNgJ3M" }
 
  | 
handlerStatus()
保存文件过程中,在回调接口中 执行此方法。- 用户在文档编辑器中编辑文档。
 - 该文档编辑器将更改到文档编辑服务。
 - 用户保存文档。
 - 文档编辑服务使用JavaScript API的callbackUrl通知文件存储服务文档编辑已经结束,并返回到修改后的文档的链接。
 - 文件存储服务从文档编辑服务下载包含所有保存的更改的文档文件,并将其存储。
 

参考项目中的:
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
   |  @RequestMapping("/onlyOffice/save") public void saveFile(HttpServletRequest request, HttpServletResponse response) {     PrintWriter writer = null;     try {         writer = response.getWriter();                  Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");         String body = scanner.hasNext() ? scanner.next() : "";         JSONObject jsonObject = JSONObject.parseObject(body);         log.info("{}", jsonObject);
          fileService.documentSave(jsonObject);     } catch (Exception e) {         e.printStackTrace();         writer.write("{\"error\":-1}");         return;     }     
 
 
      if (Objects.nonNull(writer)) {         writer.write("{\"error\":0}");     } }
  @Transactional(rollbackFor = Exception.class) public void documentSave(JSONObject jsonObject) {     String key = "";     try {         int status = jsonObject.getIntValue("status");         log.info("status[{}]", status);         if (6 == status) {             log.info("开始保存文件");
              JSONArray jsonArray = jsonObject.getJSONObject("history").getJSONArray("changes");             JSONObject object = jsonArray.getJSONObject(0);             String userId = object.getJSONObject("user").getString("id");             FileUser fileUser = new FileUser();             fileUser.setId(userId);             SecurityUtils.setUserSession(fileUser);
              key = jsonObject.getString("key");
                           String id = onlyServiceAPI.getFileId(key);
 
                           int users = onlyServiceAPI.getUserNum(key);             if (users > 1) {                 return;             }
                           Integer histNum = onlyServiceAPI.getHistNum();             
 
              if (null != histNum){                 ....             }
                           onlyServiceAPI.handlerStatus(jsonObject);
 
              log.info("保存文件结束");             SecurityUtils.removeUserSession();         } else if (0 == status || 2 == status || 4 == status) {             onlyServiceAPI.close(jsonObject);         } else if (3 == status || 7 == status) {                          onlyServiceAPI.close(jsonObject);         } else if (1 == status) {                          List<Map> actions = JSONObject.parseArray(jsonObject.getString("actions"), Map.class);             if ((Integer) actions.get(0).get("type") == 1) {                 log.info("当前用户有[{}]", jsonObject.getString("users"));                 List<String> users = JSONObject.parseArray(jsonObject.getString("users"), String.class);                 onlyServiceAPI.iskey(jsonObject.getString("key"), users.size());             }             if ((Integer) actions.get(0).get("type") == 0) {                 onlyServiceAPI.iskey(jsonObject.getString("key"), null);             }         }     } catch (Exception e) {         e.printStackTrace();         throw new RuntimeException(e);     } }
 
 
  | 
save()
外部触发保存操作。 autosave / forcesave 这俩个参数为默认值有效 。修改文件后,不执行回调方法。在点击保存后执行回调
converted()
文件转换:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | @RequestMapping("/converted")   public void converted(String id, String suffix,HttpServletResponse response) {       try {           OnFile onFile = onFileService.getById(id);           String title = onFile.getFileName().replace(onFile.getFileType(),suffix);                      String path = onlyServiceAPI.converted(onFile.getFileType(),onFile.getFileId(),suffix,title,null);
            FileUtil.downloadAttachment(path,title,response);
        } catch (Exception e) {           e.printStackTrace();       }   }
 
  | 
必须实现的接口类 SaveFileProcessor
参考:demo.service.DemoService
这个接口的实现类,会在保存回调时调用。它会把一开始的文件信息返回及文件 btye[]
这个接口一共有 3 个方法:
saveBeforeInitialization :保存前
save : 保存
saveAfterInitialization : 保存后
也可以只使用 save 方法。
自定义接口 (oo服务回调使用)
保存接口
参考:demo/controller/IndexController.saveFile()
对应配置文件参数:oo.call-back-url
打开时访问一次
如果有保存动作,访问一次
关闭时访问一次
下载文件地址
参考:demo/controller/IndexController.download()
对应配置文件参数:oo.download-file
打开文件后oo服务要下载对应的文件
前端集成
HTML
参考 onlyOffice.html
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
   |  <script type="text/javascript" th:src="${docServiceApiUrl}"></script> <script type="text/javascript" language="javascript" th:inline="javascript">     let documentChangeFlag = false;     let docEditor;     let histArray = [[${history}]];     let id = [[${id}]] ;     let fileExt = '[[${document.fileType}]]' ;
      const connectEditor = function () {         let config = {             width: "100%",             height: "100%",             type: [[${type}]],             token: [[${token}]],             documentType: [[${documentType}]],             document: [[${document}]],             editorConfig: [[${editorConfig}]],                          events: {}         }                  docEditor = new DocsAPI.DocEditor("iFrameEditor",config );     };
      if (window.addEventListener) {         window.addEventListener("load", connectEditor);     } else if (window.attachEvent) {         window.attachEvent("load", connectEditor);     }
      $(function () {
      }) </script>
 
  | 
VUE
参考 onlyOffice.vue
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
   |  /**  * 初始化文件页面信息  */ init() {   let params = this.$route.query.params;   this.id = params.id;   this.judgePlatform();   openDocument(this.id, { mode: 'edit', c: 1 }).then(res => {          if (res.code === 200) {       this.docServiceApiUrl = res.result.docServiceApiUrl;       this.token = res.result.token;       this.documentType = res.result.documentType;       this.document = res.result.document;       this.editorConfig = res.result.editorConfig;       this.fileExt = res.result.document.fileType;       this.fileName = this.document.title;
        let script = document.createElement('script');       script.type = 'text/javascript';       script.src = this.docServiceApiUrl;              document.getElementsByTagName('head')[0].appendChild(script);       this.setEditor()     } else {       this.$messagePrompt('error', res.message)     }   }) }, async setEditor() {     let config = {         width: '100%',         height: '100%',         type: this.platform,         token: this.token,         documentType: this.documentType,         document: this.document,         editorConfig: this.editorConfig,         events: {             "onAppReady": this.onReady,//-将应用程序加载到浏览器时调用的函数。             "onDocumentStateChange": this.onDocumentStateChange,//文档改变后的回调             "onError": this.onError,//发生错误或其他一些特定事件。             "onOutdatedVersion": this.onOutdatedVersion,             // "onRequestSaveAs": this.onRequestSaveAs,             "onRequestClose": this.onRequestClose,//当必须结束编辑器的工作并且必须关闭编辑器时调用的函数。             // "onDownloadAs": this.onDownloadAs         }     }     //console.log(`setEditor ==> config:`, config)     if (this.docEditor.length > 0) {         this.docEditor.destroyEditor();     }     let that = this     this.$nextTick(() => {     setTimeout(function () {         that.docEditor = new DocsAPI.DocEditor('editorDiv', config)         }, 1500);     }) },
 
  | 
Demo的运行结果
编辑文件
查看文件
格式转换