1.docker部署onlyOffice
1.1拉取最新版onlyOffice镜像
sudo docker pull onlyoffice/documentserver
1.2运行以下命令运行容器
其中 -v 后的第一部分是挂载自己的linux的哪个目录
# 启动docker容器,默认启动端口为80,可以进行修改 docker run -i -t -d -e TZ="Asia/Shanghai" -p 6831:80 --restart=always \ -v /usr/local/docker/document/logs:/var/log/onlyoffice \ -v /usr/local/docker/document/data:/var/www/onlyoffice/Data \ -v /usr/local/docker/document/lib:/var/lib/onlyoffice \ -v /usr/local/docker/document/db:/var/lib/postgresql onlyoffice/documentserver
运行完容器后开始编写代码
2.前端vue代码:
2.1public文文件夹下index.html引入对应的脚本
其中的ip地址和端口改成自己的
<script type='text/javascript' src='http://192.168.59.164:6831/web-apps/apps/api/documents/api.js'></script>
2.2 vue组件代码
<!--onlyoffice 编辑器--> <template> <div id='vabOnlyOffice'></div> </template> <script> export default { name: 'VabOnlyOffice', props: { option: { type: Object, default: () => { return {} }, }, }, data() { return { doctype: '', docEditor: null, } }, beforeDestroy() { if (this.docEditor !== null) { this.docEditor.destroyEditor(); this.docEditor = null; } }, watch: { option: { handler: function(n) { this.setEditor(n) this.doctype = this.getFileType(n.fileType) }, deep: true, }, }, mounted() { if (this.option.url) { this.setEditor(this.option) } }, methods: { async setEditor(option) { if (this.docEditor !== null) { this.docEditor.destroyEditor(); this.docEditor = null; } this.doctype = this.getFileType(option.fileType) let config = { document: { //后缀 fileType: option.fileType, key: option.key ||'', title: option.title, permissions: { edit: option.isEdit,//是否可以编辑: 只能查看,传false print: option.isPrint, download: false, // "fillForms": true,//是否可以填写表格,如果将mode参数设置为edit,则填写表单仅对文档编辑器可用。 默认值与edit或review参数的值一致。 // "review": true //跟踪变化 }, url: option.url, }, documentType: this.doctype, editorConfig: { callbackUrl: option.editUrl,//"编辑word后保存时回调的地址,这个api需要自己写了,将编辑后的文件通过这个api保存到自己想要的位置 lang: option.lang,//语言设置 //定制 customization: { autosave: false,//是否自动保存 chat: false, comments: false, help: false, // "hideRightMenu": false,//定义在第一次加载时是显示还是隐藏右侧菜单。 默认值为false //是否显示插件 plugins: false, }, user:{ id:option.user.id, name:option.user.name }, mode:option.model?option.model:'edit', }, width: '100%', height: '100%', token:option.token||'' } // eslint-disable-next-line no-undef,no-unused-vars this.docEditor = new DocsAPI.DocEditor('vabOnlyOffice', config) }, getFileType(fileType) { let docType = '' let fileTypesDoc = [ 'doc', 'docm', 'docx', 'dot', 'dotm', 'dotx', 'epub', 'fodt', 'htm', 'html', 'mht', 'odt', 'ott', 'pdf', 'rtf', 'txt', 'djvu', 'xps', ] let fileTypesCsv = [ 'csv', 'fods', 'ods', 'ots', 'xls', 'xlsm', 'xlsx', 'xlt', 'xltm', 'xltx', ] let fileTypesPPt = [ 'fodp', 'odp', 'otp', 'pot', 'potm', 'potx', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx', ] if (fileTypesDoc.includes(fileType)) { docType = 'text' } if (fileTypesCsv.includes(fileType)) { docType = 'spreadsheet' } if (fileTypesPPt.includes(fileType)) { docType = 'presentation' } return docType } }, } </script>
2.3父组件中开始引用
要记得注册组件
<div class='qualityManual-container'> <el-dialog :visible.sync="openOffice" width="800px" height="800px" append-to-body > <div class='qualityManual-container-office'> <only-office :option='option' /> </div> </el-dialog> </div>
对应的data中的属性:
openOffice:false, //参考vabOnlyOffice组件参数配置 option: { key:"", url: '', isEdit: '', fileType: '', title: '', lang: '', isPrint: '', user: { id:null,name:''} },
对应的method中添加方法(这个方法可以在需要的地方引用):
需要注意的是,这里的ip必须这样填(自己天自己的),因为作为docker运行的onlyoffice,需要这样访问本地linux服务器中的服务程序
注:(自己在本地测试时无法通过localhost进行测试运行)
modifyTem(row){ this.getFile(row.id) }, getFile(id) { this.openOffice = true this.show = false // getAction('/file/selectById', { id: this.id }).then(res => { this.option.isEdit = true // https://dsgrcdnblobprod5u3.azureedge.net/catalog-assets/zh-cn/a32a147f-1f69-4fd5-8a39-c25b2a0093e8/tf67429532_wac-987650ec15fa.docx this.option.lang = 'zh-CN' this.option.url = 'http://192.168.59.164:8080/notemode/notemodeltypeenum/download/'+id this.option.title = '笔录模板' this.option.fileType = 'docx' this.option.isPrint = false this.option.user= { id:12,name:'张三'} this.option.editUrl = "http://192.168.59.164:8080/notemode/notemodeltypeenum/save/"+id // this,option.key="11111" // }) }, close() { this.show = false },
对应的样式
<style > .qualityManual-container-office { width: 100%; /* 确保容器宽度充满整个父元素 */ height: 800px; /* 根据需要调整高度 */ overflow: hidden; /* 避免内部内容溢出 */ } </style>
3.对应的后端代码
1.dowload 方法是作为流进行文件数据的返回
2.save方法是对在线编辑后对于word的保存回调方法,在vue中有对应url回调地址的设置选项
注意:回调的时候,onlyOffice是通过一个地址(url)让你进行去下载。
有一些是测试时的代码,没有删掉,请大家注意甄别。
@GetMapping("/download/{id}") public void download(@PathVariable("id")Long id, HttpServletRequest request, HttpServletResponse response) throws Exception { Notemodeltypeenum notemodeltypeenum = notemodeltypeenumService.selectNotemodeltypeenumByID(id); String filePath = "/a.docx"; int profileIndex = notemodeltypeenum.getContentStr().indexOf("/profile") + "/profile".length(); String subPath = notemodeltypeenum.getContentStr().substring(profileIndex); filePath = RuoYiConfig.getProfile()+subPath; //本地文件 // String configPath = RuoYiConfig.getAttachPath(); // filePath = configPath + attachment.getVirtualpath(); File file = new File(filePath); if (file.exists()) { // String filename = attachment.getFilename(); response.setContentType("application/octet-stream"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("a.docx", "utf-8")); response.setCharacterEncoding("utf-8"); response.setContentLength((int) file.length()); byte[] buff = new byte[(int) file.length()]; BufferedInputStream bufferedInputStream = null; OutputStream outputStream = null; try { outputStream = response.getOutputStream(); bufferedInputStream = new BufferedInputStream(new FileInputStream(file)); int i = 0; while ((i = bufferedInputStream.read(buff)) != -1) { outputStream.write(buff, 0, i); outputStream.flush(); } } catch (IOException e) { e.printStackTrace(); } finally { try { bufferedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } @PostMapping("/save/{id}") public void save(@PathVariable("id")Long id,@RequestParam Map<String, String> map, HttpServletRequest request, HttpServletResponse response) { Notemodeltypeenum notemodeltypeenum = notemodeltypeenumService.selectNotemodeltypeenumByID(id); PrintWriter writer = null; String body = ""; try { writer = response.getWriter(); 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()) { throw new GlobalException("ONLYOFFICE回调保存请求体未空"); } JSONObject jsonObj = JSONObject.parseObject(body); int status = (Integer) jsonObj.get("status"); int saved = 0; String key = jsonObj.get("key").toString(); if (status == 2 || status == 3 || status == 6) //MustSave, Corrupted { String downloadUri = (String) jsonObj.get("url"); System.out.println(downloadUri); try { String fileName = generateUniqueFileName(extractFileName(notemodeltypeenum.getContentStr())); String savePath = RuoYiConfig.getProfile()+"/"+fileName; downloadFile(downloadUri,savePath); // 更新原数据的文件地址 String s = replacePathAfterProfile(notemodeltypeenum.getContentStr(), "/" + fileName); notemodeltypeenum.setContentStr(s); notemodeltypeenumService.updateNotemodeltypeenum(notemodeltypeenum); } catch (Exception ex) { saved = 1; ex.printStackTrace(); } } writer.write("{\"error\":" + saved + "}"); } public static void downloadFile(String fileUrl, String saveDir) { try { URL url = new URL(fileUrl); URLConnection connection = url.openConnection(); // 5000 milliseconds is an example timeout value connection.setConnectTimeout(5000); connection.setReadTimeout(5000); try (BufferedInputStream in = new BufferedInputStream(connection.getInputStream()); FileOutputStream fileOutputStream = new FileOutputStream(saveDir)) { byte[] dataBuffer = new byte[1024]; int bytesRead; while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { fileOutputStream.write(dataBuffer, 0, bytesRead); } } System.out.println("File downloaded successfully: " + saveDir); } catch (IOException e) { System.out.println("Error downloading the file: " + e.getMessage()); e.printStackTrace(); } } // // public static void main(String[] args) { // downloadFile("https://dsgrcdnblobprod5u3.azureedge.net/catalog-assets/zh-cn/a32a147f-1f69-4fd5-8a39-c25b2a0093e8/tf67429532_wac-987650ec15fa.docx","D:/新建文件夹/87650ec15fa.docx"); // } public static String generateUniqueFileName(String originalFilename) { String fileExtension = ""; // 文件扩展名 int i = originalFilename.lastIndexOf('.'); if (i > 0) { fileExtension = originalFilename.substring(i); } return UUID.randomUUID().toString() + fileExtension; // 生成带有扩展名的唯一文件名 } public static String extractFileName(String url) { // 检查url是否为空 if (url == null || url.isEmpty()) { return ""; } // 查找最后一个斜杠的位置 int lastIndex = url.lastIndexOf('/'); if (lastIndex == -1) { return ""; // 如果没有找到斜杠,返回空字符串 } // 从最后一个斜杠之后的位置开始提取子字符串 return url.substring(lastIndex + 1); } public static String replacePathAfterProfile(String url, String newPath) { if (url == null || url.isEmpty()) { return url; // 或抛出异常,取决于你的错误处理策略 } // 找到"profile"后的第一个斜杠的索引 int profileIndex = url.indexOf("/profile"); if (profileIndex == -1) { return url; // 如果找不到"profile",返回原始URL } // 计算"profile"之后的部分开始的索引 int startReplaceIndex = profileIndex + "/profile".length(); // 构造新的URL return url.substring(0, startReplaceIndex) + newPath; }
参考文章:
onlyoffice+vue实现在线预览在线编辑_onlyoffice vue-CSDN博客
SpringBoot集成onlyoffice实现word文档编辑保存-阿里云开发者社区