基于office文档(word/excel)转换为pdf格式的一站式解决方案

项目背景

公司配载系统在客户现场出现崩溃的情况。由现场客户反馈在使用配载系统的时候,偶发性出现系统卡死,停顿现象,导致无法访问。由技术人员分析后,初步得出结论,是由于引用了

        <dependency>

            <groupId>e-iceblue</groupId>

            <artifactId>spire.office.free</artifactId>

            <version>5.3.1</version>

        </dependency>

包所导致,其方法在调用Excel生成pdf方式中卡顿中断。代码demo如下参考:

  Workbook workbook = new Workbook();

                workbook.loadFromFile("src\\main\\resources\\wordTemplate\\Manifest_767_LIR_E_A_SINGLE.xlsx");

                //设置转换后的PDF页面高宽适应工作表的内容大小

                workbook.getConverterSetting().setSheetFitToPage(true);

                //将生成的文档保存到指定路径

                workbook.saveToFile("src\\main\\resources\\transferPdf\\transferResult22.pdf\\", FileFormat.PDF);

评估后发现,此jar包竟然不是开源的、而且版本比较老-jdk1.6、同时不支持反编译;那么意味着这个问题无法直接解决,方案需要重新调整,而此事也引起了严重的后果,,,,,,,此处省略五千字,最终主研离职、一代新人换旧人。于是解决历史老问题,新的解决方案也提上了日程。

方案分析

从系统业务使用场景上说,需要将前端传进来的数据转化为pdf文件,由于pdf文件除了数据还存在图片、表格以及特殊函数图表,故前一任项目组初步采用的方案是:

动态数据+excel模板->excel文件->pdf文件 

动态数据+word模板->word文件->pdf文件 

动态数据>html文件->pdf文件 

经过与技术小伙伴的探讨,以及当前项目详情的了解,初步分析得出有以下三种解决方案:

方案一:按照原版方式走

方案二:全部通过html转pdf

方案三:先手动转为pdf模板

三种方式各有优略,我们分别做可行性评估:

原版方式走

html转pdf

pdf自编辑

优点

  1. 代码修改量最少

  2. 方案比较稳定

      1、能够解决避免因闭源所引起的问题

      2、灵活性相对更高

  1. 所有包都变成pdf更新

  2. 统一编辑方便

缺点

市面上相关开源又符合项目实际的方案不多

       方案改造量大,成本高;对于已上线项目来讲风险太大,适用于新开发的系统

     动态编辑表格难度较大,完整组件普遍少

可行性

可行

是否存在未知风险:比如前端依赖包信息较大,或者html->pdf转码包可能存在其他未知坑

1、变量数据是否支持

2、市面上组件,对于包线图/平衡图的api是否支持

组件分析

收费版:spire.office.free、 appose

个人开源: excel2Pdf、 test-exceltopdf、file-convert-util-master

开源安装版: libreoffice+JODConverter、 openoffice+JODConverter

国外开源版: docx4j

国外开源:openpdf、 docx4j

个人开源:file-convert-util-master

开源安装版: libreoffice+JODConverter、 openoffice+JODConverter

国外开源:

itext、openpdf、  Apache PDFBox

方案验证

  • spire.office.free

目前老版本就是用此方案,使用如下所示:

1、引入使用包

<dependency>

    <groupId>e-iceblue</groupId>

    <artifactId>spire.office.free</artifactId>

    <version>5.3.1</version>

</dependency>

2、调用方法

如下代码所示,将excel转化为pdf

Workbook workbook = new Workbook();

workbook.loadFromFile("src\\main\\resources\\wordTemplate\\Manifest_767_LIR_E_A_SINGLE.xlsx");

//设置转换后的PDF页面高宽适应工作表的内容大小

workbook.getConverterSetting().setSheetFitToPage(true);

//将生成的文档保存到指定路径

workbook.saveToFile("src\\main\\resources\\transferPdf\\Manifest_767_LIR_E_A_SINGLE.pdf\\", FileFormat.PDF);

使用上来说,此方式比较成熟,但目前5.3.1版本较低,而且代码闭源,不可维护。官网正式版费用高,不符合小公司使用

  • aspose

1、配置引用包

aspose-cells-8.5.2.jar

aspose-words-15.8.0-jdk16.jar

2、引入license

3、使用转换工具

Workbook workbook = new Workbook("input.xlsx");

PdfSaveOptions saveOptions = new PdfSaveOptions();

saveOptions.OnePagePerSheet = true;

saveOptions.PaperSize = PaperSizeType.PaperA4;

saveOptions.Orientation = Aspose.Cells.PageOrientationType.Landscape;

saveOptions.Zoom = 90;

workbook.Save("output.pdf", saveOptions);

从使用上来说,aspose方式与spire.office.free类似。但其也是代码闭源,不可维护。官网正式版费用高,不符合小公司使用

  • excel2Pdf

这是码云上的个人开源,仅支持excel转pdf,同时整体功能还不够成熟,复杂图表无法转化,pass掉

  • test-exceltopdf

也是码云上的个人开源,与excel2Pdf类似评价

  • file-convert-util-master

码云上的个人开源,套了个aspose的壳子,而且使用还有水印,pass掉

  • libreoffice+JODConverter

1、安装libreoffice

官网下载安装版,安装完成

2、引入maven包

<!-- 引入jodconverter、libreoffice依赖转office文档pdf -->

        <dependency>

            <groupId>org.jodconverter</groupId>

            <artifactId>jodconverter-core</artifactId>

            <version>4.2.2</version>

        </dependency>

        <dependency>

            <groupId>org.jodconverter</groupId>

            <artifactId>jodconverter-local</artifactId>

            <version>4.2.2</version>

        </dependency>

        <dependency>

            <groupId>org.jodconverter</groupId>

            <artifactId>jodconverter-online</artifactId>

            <version>4.2.2</version>

        </dependency>

        <dependency>

            <groupId>org.jodconverter</groupId>

            <artifactId>jodconverter-spring-boot-starter</artifactId>

            <version>4.2.2</version>

        </dependency>

3、代码测试

import com.alibaba.fastjson.JSONObject;

import com.jky.knowledge.config.JodconverterConfig;

import org.apache.logging.log4j.LogManager;

import org.apache.logging.log4j.Logger;

import org.jodconverter.DocumentConverter;

import org.jodconverter.LocalConverter;

import org.jodconverter.OnlineConverter;

import org.jodconverter.office.LocalOfficeManager;

import org.jodconverter.office.OfficeException;

import org.jodconverter.office.OfficeManager;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Component;

import java.io.File;

import java.util.List;

/**

* office文档转PDF文档

*/

@Component

public class OfficeToPdfUtil {

    static Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);

    static LocalOfficeManager localOfficeManager = LocalOfficeManager.builder().officeHome(JodconverterConfig.getOfficeHome()).install().build();

    OfficeToPdfUtil() throws OfficeException {

        logger.info("***启动OfficeManager***");

        if(!localOfficeManager.isRunning()){

            localOfficeManager.start();

        }

    }

    /**

     * @Description office转pdf

     * @Param [fromPath--源文件, toPath--目标文件]

     * @return void

     **/

    public static void convertByLocal(File fromPath,File toPath) {

        try {

            logger.info("***开始文件转化pdf***");

            DocumentConverter converter = LocalConverter.builder().officeManager(localOfficeManager).build();

            //使用本地方式转换

            converter.convert(fromPath).to(toPath).execute();

            logger.info("***文件转化pdf成功***");

        } catch (OfficeException officeException) {

            logger.error("文件转换失败,请检查");

            officeException.printStackTrace();

        }finally {

        }

    }

    public static void stopServer() throws OfficeException {

        // Stop the office process

        localOfficeManager.stop();

        logger.info("***停止OfficeManager***");

    }

}

不过此方式依赖于安装包,在线服务使用尚可,其他则不建议使用

  • openoffice+JODConverter

跟libreoffice差不多

  • itext

外网的开源包,可以直接创建/编辑pdf,也可以将html转化为pdf。

例如将数据生产pdf代码:

package com.wh;

importjava.io.FileOutputStream;

importcom.itextpdf.text.BaseColor;

importcom.itextpdf.text.Document;

importcom.itextpdf.text.Element;

importcom.itextpdf.text.Font;

importcom.itextpdf.text.Paragraph;

importcom.itextpdf.text.Rectangle;

importcom.itextpdf.text.pdf.BaseFont;

importcom.itextpdf.text.pdf.PdfPTable;

importcom.itextpdf.text.pdf.PdfWriter;

public class ToPDF{

       // 表头

       public static final String[] tableHeader= { "姓名", "性别", "年龄",

                     "学院", "专业", "年级"};

       // 数据表字段数

       private static final int colNumber = 6;

       // 表格的设置

       private static final int spacing = 2;

       // 表格的设置

       private static final int padding = 2;

       // 导出Pdf文挡

       public static void exportPdfDocument() {

              // 创建文Pdf文挡50, 50, 50,50左右上下距离

              Document document = newDocument(new Rectangle(1500, 2000), 50, 50, 50,

                            50);

              try {

                     //使用PDFWriter进行写文件操作

                     PdfWriter.getInstance(document,new FileOutputStream(

                                   "d:\\学生信息.pdf"));

                     document.open();

                     // 中文字体

                     BaseFont bfChinese =BaseFont.createFont("STSong-Light",

                                   "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED);

                     Font fontChinese = newFont(bfChinese, 12, Font.NORMAL);

                     // 创建有colNumber(6)列的表格

                     PdfPTable datatable = newPdfPTable(colNumber);

                     //定义表格的宽度

                     int[] cellsWidth = { 8, 2,2, 8, 5, 3 };

                     datatable.setWidths(cellsWidth);

                     // 表格的宽度百分比

                     datatable.setWidthPercentage(100);

                     datatable.getDefaultCell().setPadding(padding);

                     datatable.getDefaultCell().setBorderWidth(spacing);

                     //设置表格的底色

                     datatable.getDefaultCell().setBackgroundColor(BaseColor.GREEN);

                     datatable.getDefaultCell().setHorizontalAlignment(

                                   Element.ALIGN_CENTER);

                     // 添加表头元素

                     for (int i = 0; i <colNumber; i++) {

                            datatable.addCell(newParagraph(tableHeader[i], fontChinese));

                     }

                     // 添加子元素

                     for (int i = 0; i <colNumber; i++) {

                            datatable.addCell(newParagraph(tableHeader[i], fontChinese));

                     }

                     document.add(datatable);

              } catch (Exception e) {

                     e.printStackTrace();

              }

              document.close();

       }

       public static void main(String[] args)throws Exception {

              exportPdfDocument();

       }

}

跟excel类似。不过pdf编辑图表没有,特别是复杂图表不合适。

  • openpdf

是itext的一个开源分支,可以做html到pdf的转化,也可以做pdf编辑操作。

转化代码如下:

/*

* $Id: HelloHtml.java 3373 2008-05-12 16:21:24Z xlv $

*

* This code is part of the 'OpenPDF Tutorial'.

* You can find the complete tutorial at the following address:

* https://github.com/LibrePDF/OpenPDF/wiki/Tutorial

*

* This code is distributed in the hope that it will be useful,

* but WITHOUT ANY WARRANTY; without even the implied warranty of

* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

*

*

*/

package com.lowagie.examples.html;

import com.lowagie.text.Document;

import com.lowagie.text.DocumentException;

import com.lowagie.text.html.HtmlParser;

import com.lowagie.text.pdf.PdfWriter;

import java.io.FileOutputStream;

import java.io.IOException;

/**

* Generates a simple 'Hello World' HTML page.

*

* @author blowagie

*/

public class ParseHelloHtml {

    /**

     * Generates an HTML page with the text 'Hello World'

     *

     * @param args no arguments needed here

     */

    public static void main(String[] args) {

        System.out.println("Parse Hello World");

        // step 1: creation of a document-object

        try (Document document = new Document()) {

            PdfWriter.getInstance(document, new FileOutputStream("D:\\home\\parseHelloWorld.pdf"));

            // step 2: we open the document

            document.open();

            // step 3: parsing the HTML document to convert it in PDF

            HtmlParser.parse(document, ParseHelloHtml.class.getClassLoader().getResourceAsStream("com/lowagie/examples/html/parseHelloWorld.html"));

        } catch (DocumentException | IOException de) {

            System.err.println(de.getMessage());

        }

    }

}

  • Apache PDFBox

与openPdf还有itext定位差不多,不重复操作了。

  • docx4j

1、先引入pom文件

<dependency>

    <groupId>org.docx4j</groupId>

    <artifactId>docx4j-JAXB-Internal</artifactId>

    <version>8.3.4</version>

</dependency>

<dependency>

    <groupId>org.docx4j</groupId>

    <artifactId>docx4j-JAXB-ReferenceImpl</artifactId>

    <version>8.3.4</version>

</dependency>

<dependency>

    <groupId>org.docx4j</groupId>

    <artifactId>docx4j-JAXB-MOXy</artifactId>

    <version>8.3.4</version>

</dependency>

<!--        <dependency>-->

<!--            <groupId>org.docx4j</groupId>-->

<!--            <artifactId>docx4j-export-fo</artifactId>-->

<!--            <version>8.3.4</version>-->

<!--        </dependency>-->

<dependency>

    <groupId>org.docx4j</groupId>

    <artifactId>docx4j-documents4j-local</artifactId>

    <version>8.3.4</version>

</dependency>

<dependency>

    <groupId>com.documents4j</groupId>

    <artifactId>documents4j-transformer-msoffice-excel</artifactId>

    <version>1.1.7</version>

</dependency>

<dependency>

    <groupId>org.springframework</groupId>

    <artifactId>spring-web</artifactId>

    <version>5.3.25</version>

    <scope>test</scope>

</dependency>

2、设置文件转换

package generator;

/**

* @author zhouyunjian

* @date 2024/01/09 10:07

**/

import com.documents4j.api.DocumentType;

import org.docx4j.documents4j.local.Documents4jLocalServices;

import org.springframework.web.multipart.MultipartFile;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

public class Docx4jOfficeFileToPDF {

    public static File transferToFile(MultipartFile multipartFile) {

//        选择用缓冲区来实现这个转换即使用java 创建的临时文件 使用 MultipartFile.transferto()方法 。

        File file = null;

        try {

            String originalFilename = multipartFile.getOriginalFilename();

            String[] filename = originalFilename.split("\\.");

            file=File.createTempFile(filename[0], filename[1]);

            multipartFile.transferTo(file);

            file.deleteOnExit();

        } catch (IOException e) {

            e.printStackTrace();

        }

        return file;

    }

    public static void docx2Pdf() {

        File output = new File("D:\\home\\doc4j_007_doc.pdf");

        if (!output.exists()){

            output.getParentFile().mkdirs();

        }

        FileOutputStream fos = null;

        try {

            fos = new FileOutputStream(output);

            Documents4jLocalServices   exporter = new Documents4jLocalServices();

            exporter.export(new File("D:\\home\\Manifest_LDM_CPM.docx"), fos, DocumentType.DOCX);

            fos.close();

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

//    public static String pptx2Pdf(MultipartFile file) {

//        String relativelyPath = String.format("pdf/%s.pdf", getCtm());

//        String path = CommonConfig.getFileSavePath() + relativelyPath;

//        File output = new File(path);

//        if (!output.exists()){

//            output.getParentFile().mkdirs();

//        }

//

//        FileOutputStream fos = null;

//        try {

//            fos = new FileOutputStream(output);

//

//            Documents4jLocalServices   exporter = new Documents4jLocalServices  ();

//            exporter.export(transferToFile(file), fos, DocumentType.MS_POWERPOINT);

//            fos.close();

//        } catch (Exception e) {

//            e.printStackTrace();

//        }

//        return getServerBasePath() + relativelyPath;

//

//    }

    public static void xlsx2Pdf() {

//        String relativelyPath = String.format("pdf/%s.pdf", getCtm());

//        String path = CommonConfig.getFileSavePath() + relativelyPath;

        File output = new File("D:\\home\\doc4j_006_change.pdf");

        if (!output.exists()){

            output.getParentFile().mkdirs();

        }

        FileOutputStream fos = null;

        try {

            fos = new FileOutputStream(output);

            Documents4jLocalServices   exporter = new Documents4jLocalServices();

            exporter.export(new File("D:\\home\\Manifest_737_81B_ACARS.xlsx"), fos, DocumentType.XLSX);

            fos.close();

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

    public static void html2Pdf() {

//        String relativelyPath = String.format("pdf/%s.pdf", getCtm());

//        String path = CommonConfig.getFileSavePath() + relativelyPath;

        File output = new File("D:\\home\\doc4j_006_html.pdf");

        if (!output.exists()){

            output.getParentFile().mkdirs();

        }

        FileOutputStream fos = null;

        try {

            fos = new FileOutputStream(output);

            Documents4jLocalServices   exporter = new Documents4jLocalServices();

            exporter.export(new File("D:\\home\\test.html"), fos, DocumentType.HTML);

            fos.close();

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

    public static void main(String[] args) {

       // Docx4jOfficeFileToPDF.xlsx2Pdf();

      //  Docx4jOfficeFileToPDF.docx2Pdf();

        Docx4jOfficeFileToPDF.html2Pdf();

    }

}

对比评估

如果公司有钱,绝对买:spire.office.free、 appose  这两款,功能齐全,无问题

如果公司没钱,可以用:libreoffice+JODConverter,但需要安装对应的依赖软件,部署相对麻烦

如果希望项目开源,可控:建议用 docx4j,不仅功能齐全,而且开源,部署也无额外成本

其次一些新出来的个人开源软件,例如excel2Pdf、 test-exceltopdf、file-convert-util-master 成熟度都不足,存在不少缺陷,不建议使用

纯粹的pdf调整,可以考虑用itext、openpdf、  Apache PDFBox 三兄弟

方案总结

根据当前公司情况来讲,比较适合用itext,开源、功能完善、部署方便。在查找方案的过程中,通过百度、头条、csdn、微信公总号以及一些开源网站都找过,

发现网上发的文章90%都比较坑,特别是有些没写完整的是个巨坑,无力吐槽。纸上得来终得浅,各种文章以及可行性方案验证了不下20次,基本上能验证的都验证

过了,所以写了本篇总结。希望对大家有所帮助

相关的验证分析源码:核心可以看demo

链接:https://pan.baidu.com/s/1t2CfOFtyMF4Dga7MQU2M_w?pwd=4itf

提取码:4itf

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值