java使用poi-tl导出word及转换PDF后的合并导出pdf

这篇具有很好参考价值的文章主要介绍了java使用poi-tl导出word及转换PDF后的合并导出pdf。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、背景

为某单位开发的一款项目申报审批系统,用户需求在申报阶段填写的信息资料能够导出PDF。且项目申报的报告正文为用户上传,所以需要合并导出。

2、问题

在项目初期阶段使用的是PDF的预设模板导出,因为以前使用过,比较熟悉。所以优先选择此方法,但项目测试阶段发现问题,因为某些项目的某些资料是动态的,不能确定有多少,PDF预设模板方式不够灵活,而且某些表格内容长度也是不确定的,导出效果很差。

3、解决

总体解决思路为导出word,因为有许多开源方法支持,且导出内容更灵活。满足用户数据内容长度不确定的要求。再将word转换PDF与用户上传的报告正文合并导出。

一、easypoi导出word

第一想法是想到easypoi导出word的方式。easypoi是对poi的二次封装,使得poi的多数功能得以简单实现,让许多没有接触过poi的开发者也能实现对Excel,word的导出。通过简单的注解和表达式语法实现导出功能。
引入依赖
GItHub地址
教程地址

引入依赖
		<!-- easyPOi-->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-spring-boot-starter</artifactId>
            <version>4.2.0</version>
        </dependency>
使用表达式创建模板
指令 作用
{{test ? obj:obj2}} 三元运算
n: 表示 这个cell是数值类型 {{n:}}
le: 代表长度{{le:()}} 在if/else 运用{{le:() > 8 ? obj1 : obj2}}
fd: 格式化时间 {{fd:(obj;yyyy-MM-dd)}}
fn: 格式化数字 {{fn:(obj;###.00)}}
fe: 遍历数据,创建row
!fe: 遍历数据不创建row
$fe: 下移插入,把当前行,下面的行全部下移.size()行,然后插入
#fe: 横向遍历
v_fe: 横向遍历值
!if: 删除当前列 {{!if:(test)}}
‘’ 单引号表示常量值 ‘’ 比如’1’ 那么输出的就是 1
&NULL& 空格
]] 换行符 多行遍历导出
sum: 统计数据

创建模板
java使用poi-tl导出word及转换PDF后的合并导出pdf注意模板中合租单位信息栏处,此处有坑,后续会讲到

模板上传与代码编写

模板设置好后,将文件放在项目能访问点的地方,可以是resources文件夹下。如下
java使用poi-tl导出word及转换PDF后的合并导出pdf

我系统内是将文件上传oss文件服务,通过系统配置获取文件访问URL的方式,避免修改一次导出模板就得打包一次项目。

代码编写
1、数据准备
  		dataMap.put("birth", PdfUtils.getDateMonth(vo.getSysUser().getBirthday()));//申请者出生年月
        dataMap.put("applicationPhone", vo.getSysUser().getPhone() == null ? "" : vo.getSysUser().getPhone());//申请者手机号码
        dataMap.put("applicationEmail", vo.getSysUser().getEmail() == null ? "" : vo.getSysUser().getEmail());//申请者电子邮件
        dataMap.put("companyName", vo.getDepart().getDepartName() == null ? "" : vo.getDepart().getDepartName());//申请单位--名称
        dataMap.put("companyPeople", vo.getDepart().getPeople() == null ? "" : vo.getDepart().getPeople());//申请单位-联系人
        dataMap.put("companyPhone", vo.getDepart().getMobile() == null ? "" : vo.getDepart().getMobile());//申请单位--手机
        dataMap.put("companyEmail", vo.getDepart().getEmail() == null ? "" : vo.getDepart().getEmail());//申请单位--电子邮件

        if (vo.getUnits().size() > 0) {
             dataMap.put("coorList",vo.getUnits());
        }
2、代码实现

这里的word生成是用了一个临时文件夹进行保存。使用easypoi工具类下的方法exportWord07()实现word的数据填充,方法前一个参数为模板URL,后一个为map的数据内容。因为我这里使用的oss文件存储,直接使用URL会获取不到文件。所以使用了工具类通过URL获取File。代码贴下面。

        //临时文件夹路径
        String filename = (String) dataMap.get("fileName");
        //word导出模板
        String url = wordUrl;

        File templateFile = UrlFilesToZip.Url2File(url);
        //获取模板文档
      //File templateFile = new File(url);

        //2.映射为模板
        XWPFDocument xwpfDocument = null;
        xwpfDocument = WordExportUtil.exportWord07(templateFile.getPath(), dataMap);
        //删除
        File file = new File(filename);
        for (File listFile : file.listFiles()) {
            listFile.delete();
        }
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        FileOutputStream outputStream = new FileOutputStream(filename + "/test.docx");
        xwpfDocument.write(outputStream);
通过URL获取网络文件
public static File Url2File(String url) throws Exception {
        //对本地文件命名,可以从链接截取,可以自己手写,看需求
        String fileName = "fileName";
        // String fileName = url.substring(url.lastIndexOf("."));
        File file = null;
        URL urlfile;
        InputStream inStream = null;
        OutputStream os = null;
        file = File.createTempFile("net_url", fileName);
        //下载
        urlfile = new URL(url);
        inStream = urlfile.openStream();
        os = new FileOutputStream(file);

        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        while ((bytesRead = inStream.read(buffer, 0, 8192)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        return file;
    }
方法不足

这里还没进行word转PDF的代码编写,就发现此方法的不足之处。在上面提到过合作单位信息栏。如果表格的第一列是固定的不进行遍历,而在第二列开始遍历插入。easypoi的原始方法是不满足需求的。查询后有方式可以通过修改源码实现,但没尝试,感觉麻烦,且easypoi对于富文本内容导出的处理不够完美,只能通过自己找方法实现处理。尝试过后仍然有部分样式无法保留。所以弃用此方式。

二、poi-tl导出word

通过查找,发现poi-tl的开源类库,也是基于poi开发,且对word导出的支持更好,对于导出的方式与easypoi相同,减少了关于数据准备阶段的代码修改。使用方法在参考文档中也有很多例子。

开发参考文档

依赖引入

注意版本对应,不然会出问题

<!-- POI -->		
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.2</version>
</dependency>

<!-- poi-tl -->
<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>poi-tl</artifactId>
    <version>1.10.0</version>
</dependency>
<!--poi-tl 富文本插件-->
<dependency>
   <groupId>io.github.draco1023</groupId>
   <artifactId>poi-tl-ext</artifactId>
   <version>0.4.2</version>
</dependency>
创建模板

与easypoi类似,只是在列表遍历处不一样
java使用poi-tl导出word及转换PDF后的合并导出pdf

代码编写
1、数据准备

此处的数据格式与之前的数据格式一致,无需修改

2、代码编写
public byte[] exportWordByPOi_tl(Map dataMap, String wordPath)throws Exception{
        long start = System.currentTimeMillis();
//        wordPath = "D:/Test/poi-tl/青年科学基金项目申请书导出模板.docx";
//        wordPath = "https://guizhou-keyan-oss.oss-cn-hangzhou.aliyuncs.com/temp/博士基金项目申请书导出模板_1676885802664.docx";

        //绑定行循环
        LoopRowTableRenderPolicy policy=new LoopRowTableRenderPolicy();
        //富文本插件
        HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy();
        Configure configure  = Configure.builder().bind("coorList", policy).bind("peopleList", policy).bind("peopleSingList", policy)
                .bind("fileList", policy).bind("allotList",policy).bind("equipmentList",policy).bind("budget_state",htmlRenderPolicy).build();

        //通过url获取网络文件输入流
        InputStream inputStream = POICacheManager.getFile(wordPath);

        XWPFTemplate render = XWPFTemplate.compile(inputStream,configure).render(dataMap);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        render.write(byteArrayOutputStream);
        log.info("导出word消耗时间" + (System.currentTimeMillis() - start) + "毫秒");
        render.close();
        byteArrayOutputStream.close();
        return byteArrayOutputStream.toByteArray();
    }

行循环与富文本内容处理都使用了插件,所以代码中添加了二者的写法。poi-tl类库中方法XWPFTemplate.compile不支持模板的网络url使用,所以使用了easypoi中获取网络文件输入流方法,也可以自己写一个工具实现。此处便于下一步的word转PDF,所以返回了byte数组。如果不需要下一步的处理,也可以像后文直接进行输出流返回给前端。

三、word转PDF的合并导出

此处先使用了poi的word转PDF,虽然此方法使用率很高,但是效果实在不怎么样。后来看到一个横向对比的文章
Java开发中Word转PDF文件5种方案横向评测
对比中aspose与spire的转换效果最好,本着互联网精神,最后选择aspose的方式。

依赖引入
<!--aspose 破解 word转pdf-->
        <dependency>
            <groupId>com.aspose</groupId>
            <artifactId>aspose-words</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/src/main/resources/lib/aspose-words-16.8.0-jdk16.jar</systemPath>
        </dependency>

java使用poi-tl导出word及转换PDF后的合并导出pdf

这里的jar包是下载后放置在系统resources/lib文件夹,因为做一些其他操作,所以仓库是没有这个jar包。

链接:https://pan.baidu.com/s/1k4qEQBHf-t8rco6PSWwpiQ
提取码:1446

代码编写
1、word转PDF

使用aspose转换需要进行验证

public byte[] asposeWord2Pdf(InputStream inputStream) {

        if (!getLicense()) { // 验证License 若不验证则转化出的pdf文档会有水印产生
            return null;
        }
        if (inputStream.equals(null)) {
            log.info("word为null");
            return null;
        }

        try {
            long old = System.currentTimeMillis();
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            com.aspose.words.Document doc = new com.aspose.words.Document(inputStream); //Address是将要被转化的word文档
            doc.save(os, SaveFormat.PDF);//全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换
            os.close();
            log.info("word2pdf消耗" + (System.currentTimeMillis() - old) + "毫秒");
            return os.toByteArray();
        } catch (Exception e) {
            log.info("doc转pdf文件失败,", e);
            return null;
        }
    }

license验证

public boolean getLicense() {
        boolean result = false;
        try {
            InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("wordPath/license.xml");
            License aposeLic = new License();
            aposeLic.setLicense(resourceAsStream);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

license内容,放置于resources/wordPath

<License>
    <Data>
        <Products>
            <Product>Aspose.Total for Java</Product>
            <Product>Aspose.Words for Java</Product>
        </Products>
        <EditionType>Enterprise</EditionType>
        <SubscriptionExpiry>20991231</SubscriptionExpiry>
        <LicenseExpiry>20991231</LicenseExpiry>
        <SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber>
    </Data>
    <Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature>
</License>
2、查找位置合并PDF

这里的合并我理解的是一页一页的拼接在上一页后面,不知道还有没有其他方式,至于PDF查找那一块现在我有点困,不想写了。还有俩小时下班,有人需要再补吧。合并后通过Response给前端。文章来源地址https://www.toymoban.com/news/detail-463379.html

/**
     * 合并pdf 关键词查找合并的位置
     * @Author CoCo
     * @Date 2023/2/16
     * @params
     * @return
     */
    public void mergePdf(HttpServletResponse response, byte[] pdfByte, String textUrl,String keyWord) {

        response.reset();
        response.setContentType("application/pdf");
        //response.setContentType("content-type:octet-stream;charset=UTF-8");

        try {
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(("filename").getBytes(), "iso-8859-1"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        PdfUtils pdfUtils=new PdfUtils();
        com.itextpdf.text.Document document = null;
        PdfCopy copy = null;
        OutputStream os = null;
        boolean empty = textUrl != null;
        try {
            os = response.getOutputStream();
//            File file = new File("D:/Test/aspose.pdf"); //新建一个pdf文档
//            FileOutputStream oss = new FileOutputStream(file);

            document = new com.itextpdf.text.Document(new PdfReader(pdfByte).getPageSize(1));
            copy = new PdfCopy(document, os);
            document.open();
            PdfReader pdfReader = new PdfReader(pdfByte);
            PdfReader textReader = null;
            for (int i = 1; i <= pdfReader.getNumberOfPages(); i++) {
                PdfImportedPage pdfPage = copy.getImportedPage(pdfReader, i);
                //根据关键字查询页数及其他信息
                List list = pdfUtils.matchPage(pdfReader, i, keyWord);
                //如果PDF关键字查找有返回数据且正文url不为空,拼接正文pdf
                if (!list.isEmpty()&&empty){
                    textReader = new PdfReader(UrlFilesToZip.getFileFromURL(textUrl));
                    for (int k = 1; k <= textReader.getNumberOfPages(); k++) {
                        PdfImportedPage textPage = copy.getImportedPage(textReader,k);
                        copy.addPage(textPage);
                    }
                }
                copy.addPage(pdfPage);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.info("合并失败");
        }
        finally {
            if (copy != null) {
                try {
                    copy.close();
                } catch (Exception ex) {
                    /* ignore */
                }
            }
            if (document != null) {
                try {
                    document.close();
                } catch (Exception ex) {
                    /* ignore */
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (Exception ex) {
                    /* ignore */
                }
            }
        }

    }

到了这里,关于java使用poi-tl导出word及转换PDF后的合并导出pdf的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java 使用POI-TL根据word模版,生成word文件,含图片,富文本。

    1.引入mavna坐标` 2 .poi-tl-ext插件主要用于富文本内容格式在word展现 3.word模版创建 3.具体代码实现 4.本文的miniourl路径实质为网络路径的文件。

    2024年02月16日
    浏览(52)
  • Java使用poi-tl1.9.1生成Word文档的几个小技巧

    目录 前言 一、poi-tl简介 1、什么是poi-tl 2、常见的word生成对比 3、poi-tl功能点 二、poi-tl文档生成 1、模板准备 2、目标参数填充 3、生成效果  三、可能会遇到的问题 1、混合图表生成报错 2、图表参数设置技巧  总结         也许在您的工作当中会碰到如下的一些场景,比

    2024年02月16日
    浏览(44)
  • poi-tl设置图片(通过word模板替换关键字,然后转pdf文件并下载)

    选中图片右击  选择设置图片格式   例如word模板   maven依赖   读取  代码

    2024年02月11日
    浏览(45)
  • 根据模板动态生成word(三)使用poi-tl生成word

    @ 目录 一、前言 1、什么是poi-tl 2、官方信息 2.1 源码仓库 2.2 中文文档 2.3 开源协议 3、poi-tl的优势 3.1 poi-tl和其他模板引擎的对比 3.2 poi-tl Word模板引擎支持的功能 二、基本的使用配置 1、引入依赖 1.1 Maven 1.2 Gradle 2、配置 2.1 新建配置 2.2 标签前后缀替换 2.3 加载模板 2.4 填充数

    2024年02月13日
    浏览(42)
  • 使用poi-tl向word插入图片、文本、表格行循环

    工作中难免会向word中操作数据,本文主要介绍poi-tl的使用,先来看效果图 核心介绍: 标签 1、插入文本标签 : {{var}} 2、插入图片标签: {{@var}} 操作步骤: 1、引入依赖 2、Java核心代码 官方网址:http://deepoove.com/poi-tl/ 1、准备模版,定义好需要的标签 2、查询模版 3、获取需要填

    2024年02月05日
    浏览(106)
  • Spring Boot 中使用 Poi-tl 渲染数据并生成 Word 文档

    本文 Demo 已收录到 demo-for-all-in-java 项目中,欢迎大家 star 支持!后续将持续更新! 产品经理急冲冲地走了过来。「现在需要将按这些数据生成一个 Word 报告文档,你来安排下」 项目中有这么一个需求,需要将用户填写的数据填充到一个 Word 文档中,而这个 Word 文档是人家给

    2024年02月09日
    浏览(44)
  • POI-TL制作word

    本文相当于笔记,主要根据官方文档Poi-tl Documentation和poi-tl的使用(最全详解)_JavaSupeMan的博客-CSDN博客文章进行学习(上班够用) 两个实体类 下边测试类 word模板  生成结果:   如果想生成柱状图等,按照官网进行学习即可,以上两篇(官网,和博客学习够用)

    2024年02月10日
    浏览(42)
  • JAVA poi-tl 制作word模板 表格数据行循环 带有复选框勾选的表格

            java项目实际开发中经常会遇到制作word表单且表格数据行循环功能,甚至带有复选框勾选功能,本文简单介绍如何制作模板以及使用poi-tl生成word。 提示:以下是本篇文章正文内容,下面案例可供参考 如果只用到word那么需要导入的依赖如下(本案例只需要如下2个依

    2024年04月12日
    浏览(32)
  • poi-tl的使用(动态表格的生成)

    注意apache.poi版本要对应 创建两个文件夹,一个是用来存储模板文件,另一个是用来存储生成的文件 准备一个word模板,命名为test.docx, 注意,{{}}是官方指定的格式,也可以自定义。 [ ] 是可替换的属性 可以是多个 代码 生成文件 参考: 使用最全 讲解最全

    2024年02月13日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包