vue+onlyOffice+java : 集成在线编辑word并保存

avatar
作者
筋斗云
阅读量:2

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文档编辑保存-阿里云开发者社区 

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!