Bootstrap

文档在线转PDF,Vue2接收PDF文件流预览

需求

文档在线预览功能,类似于vue-office-docx这样的插件,由于doc格式不兼容,所以体验效果不好,退而求其次,把所有文档(doc、ppt、xls等)全部在线转为PDF,再由前端接收并展示。

解决方案

使用 liberoffice 命令行将doc等文档转换为pdf文件并另存为到临时文件,vue前端接收pdf文件流并用vue-office-pdf插件完成预览。

文末提供更简便方法~

后台

pom

<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-exec</artifactId>
   <version>1.3</version>
</dependency>

controller

/**
 * 转 pdf 下载
 * @param ossId
 * @param response
 * @throws IOException
 */
@GetMapping("/downloadPdf/{ossId}")
public void downloadPdf(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
    iSysOssService.downloadToPdf(ossId,response);
}

service

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteResultHandler;
import org.apache.xmlbeans.impl.common.IOUtil;
import java.util.concurrent.Semaphore;

/**
 * 转pdf 下载
 * @param ossId
 * @param response
 * @throws IOException
 */
public void downloadToPdf(Long ossId, HttpServletResponse response) throws IOException {
    SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
    if (ObjectUtil.isNull(sysOss)) {
        throw new ServiceException("文件数据不存在!");
    }
    String pdfName = sysOss.getOriginalName().substring(0, sysOss.getOriginalName().indexOf(".")) + ".pdf";

    FileUtils.setAttachmentResponseHeader(response, pdfName);
    response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
    try {

        DefaultExecutor exec = new DefaultExecutor();
        File tempFolder = new File(System.getProperty("java.io.tmpdir"), "office2pdf-" + IdUtil.fastSimpleUUID());
        if (!tempFolder.exists()) {
            tempFolder.mkdirs();
        }
        String orginName = tempFolder.getAbsolutePath() + File.separator + sysOss.getOriginalName();
        //复制 本地
        OssClient storage = OssFactory.instance();
        try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
            int available = inputStream.available();
            OutputStream fileOutputStream = new FileOutputStream(orginName);
            IoUtil.copy(inputStream, fileOutputStream, available);
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
        // 同步等待
        Semaphore semaphore = new Semaphore(1);
        semaphore.acquire();
        ExecuteResultHandler erh = new ExecuteResultHandler() {
            @Override
            public void onProcessComplete(int i) {
                semaphore.release();
                //转换完成逻辑
            }
            @Override
            public void onProcessFailed(ExecuteException e) {
                semaphore.release();
            }
        };
        String command = "soffice --invisible --convert-to pdf --outdir \"" + tempFolder.getAbsolutePath() + "\" \""
            + orginName + "\"";
        exec.execute(CommandLine.parse(command), erh);
        // 等待执行完成
        semaphore.acquire();

        //response 下载
        File file = new File(tempFolder.getAbsolutePath() + File.separator + pdfName);
        FileInputStream inputStream = new FileInputStream(file);
        int available = inputStream.available();
        IoUtil.copy(inputStream, response.getOutputStream(), available);
        response.setContentLength(available);
    } catch (Exception e) {
        throw new ServiceException(e.getMessage());
    }
}

前台

// pdf文档预览组件

npm install @vue-office/pdf

<template>  
    <!--   pdf 直接预览   -->
      <div>
        <vue-office-pdf
          :src="pdfUrl"
          @rendered="renderedHandler"
          @error="errorHandler"
        />
      </div>
</template>

//引入VueOfficePdf组件
import VueOfficePdf from '@vue-office/pdf'

export default {
  components:{
    VueOfficePdf,
  },
  data() {
    return {
      pdfUrl: "",
      fullscreenLoading: false,
    }
  },
  methods: {
    /** pdf */
    renderedHandler() {
      console.log("渲染完成")
      this.fullscreenLoading = false
    },
    errorHandler() {
      console.log("渲染失败")
      this.fullscreenLoading = false
    },
    /** 在线转PDF */
    ossPdf(ossId) {

      this.fullscreenLoading = true
      var url = process.env.VUE_APP_BASE_API + '/file/downloadPdf/' + ossId
      axios({
        method: 'get',
        url: url,
        responseType: 'blob',
        headers: { 'Authorization': 'Bearer ' + getToken() }
      }).then((res) => {
        const isBlob = blobValidate(res.data);
        if (isBlob) {

          let fileURL = null
          var blob = new Blob([res.data], {type: 'application/pdf'})
          if (window.createObjectURL != undefined) { // basic
            fileURL = window.createObjectURL(blob);
          }else if (window.webkitURL != undefined) { // webkit or chrome
            try {
              fileURL = window.webkitURL.createObjectURL(blob);
            } catch (error) {console.log(error)}
          } else if (window.URL != undefined) { // mozilla(firefox)
            try {
              fileURL = window.URL.createObjectURL(blob);
            } catch (error) {console.log(error)}
          }
          console.log("PDF文件地址0 ", fileURL)
          this.pdfUrl = fileURL

        } else {
          this.printErrMsg(res.data);
        }
      }).catch((r) => {
        console.error(r)
        Message.error('下载文件出现错误,请联系管理员!')
      })
    },
}

扩展

kkFileView-文件文档在线预览

该项目使用流行的spring boot搭建,易上手和部署,基本支持主流办公文档的在线预览,如doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,rar,图片,视频,音频等等

docker安装并运行

需要预览文件时,只需要调用浏览器打开本项目的预览接口,并传入须要预览文件的url

;