OnlyOffice pdf打印&下载流程改造

821 阅读3分钟

OnlyOffice pdf打印&下载流程改造

背景

在网页打开office文档时会去请求字体文件,而字体文件又比较大,导致初始化速度较慢,但是不请求采用服务端字体而是采用浏览器支持字体时,会出现打印和下载pdf时中文无法正常渲染,类似“[][][][]"。

解决方案

借助外部office进行将传统office(docx/xlsx/pptx)进行pdf转换,再将原来的下载链接返回给前端,这样前端只需再发起下载和打印的请求下添加一个服务端可识别的参数即可。

外部office采用的是libreoffice,是一个开源的office套件,与微软office相容性很好,可以用来做office->pdf转换。

服务改造

  1. 首先前端在发起下载&打印pdf时,均发起原来对应下载的docx/xlsx/pptx请求,并在原来的请求体上增加metadata参数,用printfile和downloadPdf进行区分
{"c":"save","id":"3xG.docx_v2","userid":"1659334866298","outputformat":65,"title":"文字文档.docx","nobase64":true,"withoutPassword":true,"inline":1,"metadata":{"officeToPdf":"printfile"},"savetype":3,"saveindex":1,"userconnectionid":"16593348662983"}
  1. 改造onlyoffice-server项目。因为下载打印的流程类似,均为发起同一个请求,当文件准备好后通过ws将打印或下载的url返回给前端,前端通过url请求文件。那将原来的请求修改为打印下载office通过metadata区分是否需要转换,转换后通过ws返回一个新的地址即可。

    1. 响应的InputCommand实体类增加metadata属性

    2. 在原来ws返回的地方增加对metadata的判断,通过libreoffice进行转换

      1. 原office文件地址
configStorage.get('fs.folderPath') + cmd.getSaveKey()+ cmd.getOutputPath(); 
即/var/lib/onlyoffice/documentserver/App_Data/cache/files/25u.docx_v1_4676/output.[docx/pptx/xlsx]
      1. libreoffice文件转换(node.js通过起一个子进程去执行libreoffice转换脚本),pdf和原来同一个路径即可
const util = require('util');  //用来提供常用函数的集合
const exec = util.promisify(require('child_process').exec);
。。。
async function officeToPdf(officePath, pdfPath, callback) {
  try {
    const { stdout, stderr } = await exec('libreoffice --headless --convert-to pdf --outdir ' + pdfPath + ' ' + officePath);
    if (stderr) {
      logger.info('stderr:', stderr);
      callback(false);
    }
    logger.info('stdout:', stdout);
    callback(true)
  } catch (e) {
    logger.error('officeToPdf error %s', e)
    callback(false)
  }
}
  1. 替换原来的url,设置为对应下载或者打印
                   if (metadata.officeToPdf == 'downloadpdf') {
                      // newUrl: https://testdemooffice.xxx.com/downloadpdf/25b.docx_v2_1017/文字文档.pdf
                      let newUrl = baseUrl + '/downloadpdf/' + cmd.getSaveKey() + '/' + filename
                      outputData.setData(newUrl)
                    } else if (metadata.officeToPdf == 'printfile') {
                      // newUrl: https://demooffice.xxx.com/printfile/3xG.docx_v1_7008/文字文档.pdf?filename=文字文档.pdf
                      let newUrl = baseUrl + '/printfile/' +  cmd.getSaveKey() + '/' + filename + '?filename=' + filename
                      outputData.setData(newUrl)
                    }
  1. 新增下载pdf的接口,对于打印来说用原接口即可
下载与打印其实是一样的,都是请求文件流,不过在请求头上一个表示附件,一个是打开
      res.setHeader('Content-Disposition', utils.getContentDisposition(filename, null, constants.CONTENT_DISPOSITION_ATTACHMENT));

DockerFile镜像改造

安装libreoffice至镜像

拷贝windows字体文件到Linux环境

设置文件夹权限,否则libreoffice命令会执行失败

...
RUN apt-get update && apt-get -y install libreoffice
...
COPY ./fonts/* /usr/share/fonts/
...
RUN chmod 777 -R /var/www/onlyoffice/documentserver
RUN chown -R ds /var/www/onlyoffice/documentserver

遗留问题

镜像构建很慢

对于超大表格打印下载pdf貌似有问题

错误提示前端适配