项目地址
快速使用
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的运行结果
编辑文件
查看文件
格式转换