完成了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>
Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐