JAVA转OFFICE(word、excel、ppt)文件为PDF格式

技术选型:spring cloud + vue + nginx

需求:前端上传文件到服务器后可以实现下载与预览

问题与解决方案:因为前端的兼容性等问题、对于文件预览不是很友好、而且实现也都比较复杂、从而想通过后端将文件转化成pdf、前端通过vue的pdf.js插件实现预览效果、但是java对office文件的转化pdf支持性不是很好、比较好的jar还收费、由此利用java调用python脚本进行实现pdf文件的转换。

实现逻辑:前端已将文件上传到服务器、点击预览按钮时文件生成pdf文件并返回文件路径

java:

/**
 * OFFICE文件转PDF方法
 *
 * @author  liangyy
 * @Date 2021/4/2 15:06
 */
public class TurnPDFUtil {

    /**
     * 文件转pdf
     * @param path 文件保存路径
     * @param name 文件名称
     * @param pythonPath  python安装路径
     */
    public static void turnPDF(String path,String name,String pythonPath){
        try {
            String fileType = name.substring(name.lastIndexOf(StringConstant.DOT)+1).toUpperCase();
            String oldName = name.substring(0,name.lastIndexOf(StringConstant.DOT));
            String excelToPDFPY = getPDFPY(fileType);
            // 获取文件夹名称
            String folderName = "file";
            // 生成文件名
            String pdfPath = path + folderName + File.separator + oldName + FebsConstant.PDF_SUFFIX;
            // 脚本路径
            String excelToPDFToosPath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + File.separator + FebsConstant.RESOURCES_PY + File.separator + excelToPDFPY;
            File file = new File(path + folderName + File.separator + name);
            List<String> command = Lists.newArrayList();
            command.add(pythonPath);
            command.add(excelToPDFToosPath.replaceFirst("/",""));
            command.add(file.getAbsolutePath());
            command.add(pdfPath);
            ExecuteShellUtil.execute(command);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 获取文件类型
     * @param fileType
     * @return
     */
    private static String getPDFPY(String fileType){
        if(FebsConstant.EXCEL.indexOf(fileType) > -1){
            return "excelToPDF.py";
        }else if(FebsConstant.WORD.indexOf(fileType) > -1){
            return "wordToPDF.py";
        }else if(FebsConstant.PPT.indexOf(fileType) > -1){
            return "pptToPDF.py";
        }
        return null;
    }
}
/**
 * 操作系统进程
 *
 * @author liangyy
 * @date 2021-03-31 10:20:07
 */
public class ExecuteShellUtil {

    /**
     * 执行(java启动系统进程时,启动成功后就直接返回了,并不会等待系统进程执行结束,这里我们需要等待系统进程调用结束后java方法再返回)
     * @param command
     */
    public static void execute(List<String> command) {
        try {
            // 创建系统进程
            ProcessBuilder processBuilder = new ProcessBuilder();
            // 设置系统进程要执行的系统程序和参数
            processBuilder.command(command);
            // 使用此进程生成器的属性启动一个新进程
            Process process = processBuilder.start();
            dealWith(process);
            try {
                // 等待子进程的结束,子进程就是系统调用文件转换这个新进程
                process.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理进程的IO防止出现阻塞、死锁等情况
     * @param pro
     */
    private static void dealWith(final Process pro) {
        // 下面是处理堵塞的情况
        try {
            // 启动单独线程来清空pro.getInputStream()的缓冲区
            new Thread() {
                @Override
                public void run() {
                    BufferedReader br1 = new BufferedReader(new InputStreamReader(pro.getInputStream()));
                    try {
                        String text;
                        while ((text = br1.readLine()) != null) {
                            System.out.println(text);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            // 不要忘记处理出理时产生的错误信息,不然会堵塞不前的
            new Thread() {
                @Override
                public void run() {
                    BufferedReader br2 = new BufferedReader(new InputStreamReader(pro.getErrorStream()));
                    String text;
                    try {
                        while ((text = br2.readLine()) != null) {
                            System.err.println(text);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

python脚本:

下面是三个脚本文件

python安装如下两个模块:
pip install comtypes
pip install pypiwin32

excelToPDF.py:

# coding=utf-8
from sys import argv
from win32com import client

path = argv[1]
pdf_path = argv[2]

xlApp = client.Dispatch("Excel.Application")
books = xlApp.Workbooks.Open(path)
books.ExportAsFixedFormat(0, pdf_path)
books.Close(SaveChanges=0)
xlApp.Quit()

pptToPDF.py:

# coding=utf-8
from sys import argv
from comtypes.client import CreateObject

path = argv[1]
pdf_path = argv[2]

powerpoint = CreateObject("Powerpoint.Application")
powerpoint.Visible = 1
pdfCreate = powerpoint.Presentations.Open(path)
pdfCreate.SaveAs(pdf_path, 32)
pdfCreate.Close()
powerpoint.Quit()

wordToPDF.py:

# coding=utf-8
from sys import argv
from comtypes.client import CreateObject

path = argv[1]
pdf_path = argv[2]

wdToPDF = CreateObject("Word.Application")
pdfCreate = wdToPDF.Documents.Open(path)
pdfCreate.SaveAs(pdf_path, 17)
pdfCreate.Close()
wdToPDF.Quit()

java后端处理完毕。

注:文件转换直接调用TurnPDFUtil.turnPDF()方法,因为是在ServiceImpl中调用了该方法,文件返回路径由service直接替换后缀返回数据,所以在turnPDF方法中没有返回,根据你自己的逻辑修改该方法。该文件路径是由nginx所映射的路径。用python脚本时,先搭建python环境,然后在安装python脚本需要用的两个模块。在java代码中要把python的安装路径传到方法中,脚本的路径根据你自己的实际路径去修改修改配置

vue:

先执行下载 pdf.js 指令

npm install --save vue-pdf

预览页面引用 pdf.js

import pdf from 'vue-pdf'
<template>
  <div>
    <pdf
      v-for="i in numPages"
      ref="pdf"
      :key="i"
      :src="url"
      :page="i"
    />
  </div>
</template>
<script>
import pdf from 'vue-pdf'

export default {
  name: 'PdfView',
  components: {
    pdf
  },
  data() {
    return {
    	url: null,
    	numPages: null
    }
  },
  // 每次进入页面时请求
  activated() {
     this.getNumPages()
  },
  methods: {
    getNumPages() {
        // 计算PDF页数
        var loadingTask = pdf.createLoadingTask(this.url)
        loadingTask.promise.then(pdf => {
          this.numPages = pdf.numPages
          loading.close()
        }).catch(err => {
          console.error(err)
          loading.close()
          this.$message({
            message: this.$t('website.home.pdfOpenError'),
            type: 'error'
          })
       })
    }
  }
}
</script>
<style lang="scss" scoped>
</style>

前端处理完毕。

注:该处是用router,带了一个文件的id,请求后端方法来查找该文件的映射路径,跳转的方法此处省略,跳转到预览路径后,调用getNumPages()方法,this.url是文件路径。

你可能感兴趣的:(java,java)