首页 前端知识 vue onlyOffice java : 集成在线编辑word并保存

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

2024-06-22 10:06:15 前端知识 前端哥 845 58 我要收藏

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

转载请注明出处或者链接地址:https://www.qianduange.cn//article/13318.html
标签
评论
发布的文章

JMeter断言之JSON断言

2024-07-22 01:07:18

39 | XML、JSON、YAML比较

2024-07-22 01:07:13

String和JSON相互转换

2024-07-22 01:07:11

npm 安装报错

2024-07-22 01:07:55

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!