java+vue+onlyoffice的简单集成_onlyoffice/document-editor-vue 使用-程序员宅基地

技术标签: java  vue+onlyoffice  vue-js  在线文档编辑  

完成了springboot+vue+onlyoffice的集成,实现用户上传文件,编辑文件的基本操作。
后续将完成协作编辑,版本管理,文件加密,解密打开等相关操作。

文件界面实例图:
在这里插入图片描述

在这里插入图片描述

1、部署onlyoffice的docker

docker run -i -t -d --name onlyoffice  --privileged  -p 9999:80 -p 5432:5432 --restart=always -v /e/publish/onlyoffice/DocumentServer/logs:/var/log/onlyoffice  -v /e/publish/onlyoffice/DocumentServer/data:/var/www/onlyoffice/Data  -v /e/publish/onlyoffice/DocumentServer/lib:/var/lib/onlyoffice -v /e/publish/onlyoffice/DocumentServer/db:/var/lib/postgresql -v /e/work/gznew/seal:/var/www/onlyoffice/documentserver/sdkjs-plugins/seal onlyoffice/documentserver

-v /e/work/gznew/seal:/var/www/onlyoffice/documentserver/sdkjs-plugins/seal 是将自己写的签章插件挂载到插件目录

2、编辑器控件

<!--onlyoffice 编辑器-->
<template>
  <div id="editorDiv"></div>
</template>

<script>
import {
     handleDocType } from '@/utils/onlyofficeUtil'

export default {
    
  name: 'editor',
  props: {
    
    option: {
    
      type: Object,
      default: () => {
    
        return {
    }
      }
    }
  },
  data() {
    
    return {
    
      doctype: ''
    }
  },
  mounted() {
    
    if (this.option.url) {
    
      this.setEditor(this.option)
    }
  },
  methods: {
    
    setEditor(option) {
    
      this.doctype = handleDocType(option.fileType)
      let config = {
    
        document: {
    
          fileType: option.fileType,
          key: option.key,
          title: option.title,
          permissions: {
    
            comment: true,
            download: true,
            modifyContentControl: true,
            modifyFilter: true,
            print: false,
            edit: option.isEdit,
            fillForms: true,
            review: true
          },
          url: option.url
        },
        documentType: this.doctype,
        editorConfig: {
    
          callbackUrl: option.editUrl,
          lang: 'zh',
          customization: {
    
            commentAuthorOnly: false,
            comments: true,
            compactHeader:false,
            compactToolbar:true,
            feedback:false,
            plugins:true
          },
          user:{
    
            id:option.user.id,
            name:option.user.name
          },
          mode:option.model?option.model:'edit',

        },
        width: '100%',
        height: '100%',
        token:option.token
      }
      // console.log(config)
      let docEditor = new DocsAPI.DocEditor('editorDiv', config)
    },
  },
  watch: {
    
    option: {
    
      handler: function (n, o) {
    
        this.setEditor(n)
        this.doctype = handleDocType(n.fileType)
      },
      deep: true,
    }
  }
}
</script>

<style scoped>

</style>

3、控件中用到的方法,判断文件类型

export function handleDocType(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;
}

4、用一个控件来引用编辑器控件,接收文件参数,并组织编辑器控件参数

<template>
  <editor :option="option"></editor>
</template>

<script>
import {
     getAction } from '@/api/manage'
import editor from './editor'
import Vue from 'vue'
import {
     ACCESS_TOKEN, USER_INFO } from '@/store/mutation-types'

export default {
    
  name: 'fView',
  components: {
     editor },
  data() {
    
    return {
    
      id: this.$route.params.id,
      option: {
    
        url: '',
        isEdit: true,
        fileType: '',
        title: '',
        token: Vue.ls.get(ACCESS_TOKEN),
        user: {
    
          id: '',
          name: ''
        },
        mode: 'edit',
        editUrl: '',
        key: ''
      },
    }
  },
  created() {
    
    let userInfo = Vue.ls.get(USER_INFO)
    this.option.user.id = userInfo.id
    this.option.user.name = userInfo.realname
    this.getFile()
  },
  methods: {
    
    getFile() {
    
      getAction('/onlyfile/file/queryById', {
     id: this.id }).then(res => {
    
        this.option.url = window._CONFIG['officeUrl'] + '/' + res.result.fileUrl
        this.option.editUrl = window._CONFIG['callBackUrl'] + '?userId=' + this.option.user.id + '&fileId=' + this.id
        this.option.title = res.result.fileName
        this.option.fileType = res.result.fileExt
      })
    }
  },
  watch: {
    
    '$route'(to, from) {
    
      this.id = to.params.id
      this.getFile()
    }
  }
}
</script>

<style scoped>

</style>

注意:key 不用设置,会自动分配一个,如果用文件id设置为key,则打开文件时,会用key在文档服务器中寻找文件,已有的key会直接打开,且不可编辑。
window._CONFIG的定义:

//在线文档后端地址
Vue.prototype.OFFICE_API_URL = process.env.VUE_APP_OFFICE_API_URL
window._CONFIG['officeUrl'] = Vue.prototype.OFFICE_API_URL + '/sys/common/static'  //上传地址
window._CONFIG['callBackUrl'] = Vue.prototype.OFFICE_API_URL + '/onlyfile/file/editCallBack'  //编辑器回调地址

.env文件中
VUE_APP_OFFICE_API_URL=http://192.168.124.200:8080

5、将这个控件配置一个路由地址,就可以使用了
router.config.js中

  {
    
    //在线文件编辑
    path: '/fileEditor/:id',
    component: () => import('@/components/onlyoffice/fView')
  },

使用:

goEditor(id){
    
      let routeUrl = this.$router.resolve({
    
          path: "/fileEditor/"+id
        });
        window.open(routeUrl.href,'_blank')
      }

参数id 是文件的id

6、后端回调地址:

 /**
     * 在线编辑器回调操作
     */
    @RequestMapping(path = "/editCallBack", method = {
    RequestMethod.POST, RequestMethod.GET})
    public void editCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException {
    
        PrintWriter writer = response.getWriter();
        Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
        String body = scanner.hasNext() ? scanner.next() : "";
        JSONObject jsonObj = JSONObject.parseObject(body);

        String userId = request.getParameter("userId");
        String fileId = request.getParameter("fileId");

        log.info("在线编辑器回调参数:{}", jsonObj);

        if ((Integer) jsonObj.get("status") == 2) {
    
            String downUrl = (String) jsonObj.get("url"); //保存变更后文件
            String changeUrl = (String) jsonObj.get("changesurl"); //保存文件的修改记录
            String lastsave = (String) jsonObj.get("lastsave");

            try {
    
                fileChangeService.saveChange(downUrl, changeUrl, fileId, userId, lastsave);
            } catch (Exception e) {
    
                log.info("保存回调文件出错:{}", e.getMessage());
            }
        }

        writer.write("{\"error\":0}");
    }

最后需要返回:“error”:0 ,否则编辑器会出现错误。

7、saveChange 方法,下载文档服务器中最新版文件,覆盖到原文件地址,将原文件备份为历史文件,保存修改记录

  @Value(value = "${jeecg.path.upload}")
    private String uploadpath;

    @Override
    public void saveChange(String downUrl, String changeUrl, String fileId, String userId, String changeTime) throws IOException {
    
        File entity = fileService.getById(fileId);
        String bizPath = "office" + java.io.File.separator + entity.getUserId();
        String fullName = entity.getFileUrl().substring(bizPath.length() + 1); //文件全名
        String fName = fullName.substring(0, fullName.lastIndexOf(".")); //文件名
        String hisPath = bizPath + java.io.File.separator + fName;
        String hisFile = hisPath + java.io.File.separator + entity.getFileName() + "_" + System.currentTimeMillis() + "." + entity.getFileExt();
        String zipFile = hisFile + ".zip";

        java.io.File dirHisPath = new java.io.File(hisPath);
        if (!dirHisPath.exists()) {
    
            dirHisPath.mkdir();
        }

        //复制保存旧文件
        java.io.File oldFile = new java.io.File(uploadpath + java.io.File.separator + bizPath + java.io.File.separator + fullName);
        java.io.File dstFile = new java.io.File(uploadpath + java.io.File.separator + hisFile);
        FileUtil.copy(oldFile, dstFile, true);

        try {
    
            URL url = new URL(downUrl);
            URL zipUrl = new URL(changeUrl);

            try {
    
                //新版文件
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                InputStream inputStream = connection.getInputStream();
                FileOutputStream fileOutputStream = new FileOutputStream(oldFile);
                int read = 0;
                final byte[] bytes = new byte[1024];
                while ((read = inputStream.read(bytes)) != -1) {
    
                    fileOutputStream.write(bytes, 0, read);
                }
                fileOutputStream.flush();
                fileOutputStream.close();
                connection.disconnect();
                //变更记录
                HttpURLConnection urlConnection = (HttpURLConnection) zipUrl.openConnection();
                InputStream inputStream1 = urlConnection.getInputStream();
                java.io.File changeFile = new java.io.File(uploadpath + java.io.File.separator + zipFile);
                FileOutputStream fileOutputStream1 = new FileOutputStream(changeFile);
                int readZip = 0;
                byte[] buffer = new byte[1024];
                while ((readZip = inputStream1.read(buffer)) != -1) {
    
                    fileOutputStream1.write(buffer, 0, readZip);
                }
                fileOutputStream1.flush();
                fileOutputStream1.close();
                urlConnection.disconnect();

                inputStream.close();
                inputStream1.close();


            } catch (IOException e) {
    
                throw e;
            }
        } catch (MalformedURLException e) {
    
            throw e;
        }

        FileChange fileChange = new FileChange();
        fileChange.setUserId(userId);
        fileChange.setFileId(fileId);
        fileChange.setHisFileUrl(hisFile);
        fileChange.setHisChangeUrl(zipFile);
        try {
    
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
            simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
            Date parse =simpleDateFormat.parse(changeTime);
            fileChange.setChangeTime(parse);
        } catch (ParseException e) {
    
            fileChange.setChangeTime(new Date());
        }

        this.save(fileChange);


    }

很重要的一个给忘了,需要在前端的index.html中,包含进onlyoffice的js地址

<script type="text/javascript" src="http://192.168.124.200:9999/web-apps/apps/api/documents/api.js"></script>
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wyljz/article/details/113996147

智能推荐

海量处理问题的解决方案_给定一个大文件100g,其中包含-程序员宅基地

文章浏览阅读357次。1. 给定一个大小超过 100G 的文件, 其中存在 IP 地址, 找到其中出现次数最多的 IP 地址(hash文件切分) 2. 给定100亿个整数, 找到其中只出现一次的整数(位图变形, 用两位来表示次数). 3. 有两个文件, 分别有100亿个query(查询词, 字符串), 只有1G内存, 找到两个文件的交集(hash文件切分 + 布隆过滤器). 4. 给上千个文件, 每个文件大小为1K -..._给定一个大文件100g,其中包含

黑马程序员___单例设计模式-程序员宅基地

文章浏览阅读449次。------- android培训、java培训、期待与您交流! ---------- 设计模式:解决某一类问题最行之有效的方法。Java中23种设计模式:单例模式:解决一个类在内存中只存在一个对象。想要保证对象唯一●为了避免其它程序过多建立该类对象,先禁止其他程序建立该对象。●还为了让其他程序可以访问到该类对象,只好在本类中自定义一个对

vscode 前端配置编辑器(vue)_vscode vue 页面编辑器-程序员宅基地

文章浏览阅读414次。最后配置格式化json,没有绝对标准,主要是要团队统一。第2和第5配合可以实现css代码用颜色区分。_vscode vue 页面编辑器

PyTorch-hook_pytorch hook-程序员宅基地

文章浏览阅读633次。钩子编程(hooking),也称作“挂钩”,是计算机程序设计术语,指通过拦截软件模块间的函数调用、消息传递、事件传递来修改或扩展操作系统、应用程序或其他软件组件的行为的各种技术。处理被拦截的函数调用、事件、消息的代码,被称为钩子(hook)。Hook 是 PyTorch 中一个十分有用的特性。利用它,我们可以不必改变网络输入输出的结构,方便地获取、改变网络中间层变量的值和梯度。这个功能被广泛用..._pytorch hook

ios--根据url获取图片宽高_ios 图片url 如何知道高度-程序员宅基地

文章浏览阅读3.1k次。NSString *urlStr = [data.imgobjectAtIndex:0]; NSURL *imageUrl = [NSURLURLWithString:urlStr]; NSData *imageData = [NSDatadataWithContentsOfURL:imageUrl]; _ios 图片url 如何知道高度

WIN10 升级SSD硬盘 BOIS调整boot manager 顺序_bootmanger里没有固态硬盘-程序员宅基地

文章浏览阅读1w次。WIN10 升级了SSD硬盘,使用分区助手 将C盘拷贝到SSD硬盘这一步骤网上很多教程,不再细说请看链接http://www.w10zj.com/Win10xy/Win10yh_4851.html有很多电脑无法在BOIS 启动顺序里面是boot manager ,没有SSD硬盘选项本人经过自行分析找到了另外的方法:使用bcdedit修改的方法:1.右键单击桌面右下角的【..._bootmanger里没有固态硬盘

随便推点

/tmp/cc4QQlah.o:在函数‘main’中: web.c:(.text+0x255):对‘peeror’未定义的引用_/tmp/ccesx7q9.o:在函数‘main’中:-程序员宅基地

文章浏览阅读3.1k次。root@wangmiao:~/lesson# vim web.croot@wangmiao:~/lesson# vim wrap.hroot@wangmiao:~/lesson# vim wrap.croot@wangmiao:~/lesson# gcc web.c wrap.c -o webweb.c:9:20: fatal error: singnal.h: 没有那个文件或目录 #inclu..._/tmp/ccesx7q9.o:在函数‘main’中:

mac下安装php-redis扩展_mac php redis.os 下载-程序员宅基地

文章浏览阅读9.1k次。mac下安装php-redis扩展操作系统版本:10.12.5。下载php-redis,地址:https://nodeload.github.com/nicolasff/phpredis/zip/master下载完成得到phpredis-master.zip移动到/usr/local/目录中sudo cp phpredis-master /usr/local/进入/usr/local/目录cd /u_mac php redis.os 下载

SOFA企业应用框架_阿里巴巴sofa框架运行起来后怎么调用-程序员宅基地

文章浏览阅读381次。前言从业这么多年,接触过银行的应用,Apple的应用,eBay的应用和现在阿里的应用,虽然分属于不同的公司,使用了不同的架构,但有一个共同点就是都很复杂。导致复杂性的原因有很多,如果从架构的层面看,主要有两点,一个是架构设计过于复杂,层次太多能把人绕晕。另一个是根本就没架构,ServiceImpl作为上帝类包揽一切,一杆捅到DAO(就简单场景而言,这种Transaction Script也还凑合,..._阿里巴巴sofa框架运行起来后怎么调用

[转载]Jboss中服务配置文件jboss-service.xml_习惯累积沉淀_新浪博客-程序员宅基地

文章浏览阅读1.1k次。有次oracle还把1098端口占用了原文地址:Jboss中服务配置文件jboss-service.xml作者:kingJboss中服务配置文件jboss-service.xml2007-11-22 23:33:31转自网友:JBOSS默认的各种设置文件中通常定义了以下几个端口,1: The ports found in the default configuration ..._jboss-service.xm

mpvue 中input框 回车 触发 confirm事件_input confirm事件-程序员宅基地

文章浏览阅读2.6k次。mpvue 中input框 回车 触发 confirm事件在输入框 按下回车后,保存关键字,并放入本地存储input绑定的confirm事件<input v-model="keyword" @confirm="saveKeyword" @input="keywordSearch" placeholder="请输入关键字" />在data中声明的数据data(){ ..._input confirm事件

学习 opencv---(10)形态学图像处理(2):开运算,闭运算,形态学梯度,顶帽,黒帽合辑...-程序员宅基地

文章浏览阅读946次。上篇文章中,我们重点了解了腐蚀和膨胀这两种最基本的形态学操作,而运用这两个基本操作,我们可以实现更高级的形态学变换。 所以,本文的主角是OpenCV中的morphologyEx函数,它利用基本的膨胀和腐蚀技术,来执行更加高级的形态学变换,如开闭运算、形态学梯度、“顶帽”、“黑帽”等等。第二件事,是浅墨想跟大家做一个关于OpenCV系列文章的书写内容和风格的思想汇..._opening operation

推荐文章

热门文章

相关标签