poi-tl导出复杂word/pdf文档(去除aspose转pdf水印,多张图片、表格循环行合并列、表格带图片、循环多个模板导出,多个文档合并)

首先奉上poi-tl官方文档地址http://deepoove.com/poi-tl/#_%E7%89%88%E6%9C%AC
1.导包

gradle:
//有了poi-tl的依赖也需要poi的相关依赖,用以下版本可以避免或多或少的版本冲突问题
//少了poi的依赖可能报错java.lang.NoSuchMethodError: org.apache.poi.util.IOUtils.toByteArray(Ljava/io/InputStream;II)[B""
implementation 'com.deepoove:poi-tl:1.10.5'
implementation  "org.apache.poi:poi:4.1.2"
<--一些poi的依赖-->
implementation group: 'org.apache.poi', name: 'ooxml-schemas', version: '1.4'
implementation "org.apache.poi:poi-ooxml:4.1.2"
<--aspose的依赖包-->
文件资源:https://download.csdn.net/download/m0_49605579/59212077 (不需要C币,免费,有说文件资源下载需要密码的,你做个任务白嫖就好了呀)

注:注意poi-tl的版本,不同版本之间对应的poi版本不一样,api也不一样,所以需要参考文档时要选择对应的版本文档,不然就会报一系列的异常。

配置结构(这个是转pdf的依赖包,如果只需要生成word格式文件则不需要此依赖包,代码对应删除转换pdf的逻辑即可)
在这里插入图片描述

2.创建模板
模板样式任意(如下图),只需填充占位符即可,循环图片也可以使用{{?list}}{{@#this}} {{/list}}代码少写个Map
![在这里插入图片描述](https://img-blog.csdnimg.cn/684f17b06fb64e71b37ac6f8d0f6a6c5.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5bCP5bCP6IqxX18=,size_20,color_FFFFFF,t_70,g_se,x_16

3.编写破解pdf水印方法

package com.yswl.upms.util;


import com.aspose.words.Document;
import com.aspose.words.License;
import com.aspose.words.SaveFormat;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @Author 955
 * @Date 2022-03-08 16:07
 * @Description 导入导出工具类
 */
public class Word2PdfAsposeUtil {



    public static boolean getLicense(InputStream inputStream) {
        boolean result = false;
        InputStream is = null;
        try {
            is=inputStream;
            License aposeLic = new License();
            aposeLic.setLicense(is);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return result;
    }

    public static boolean doc2pdf(String inPath, String outPath,InputStream inputStream) {
        if (!getLicense(inputStream)) { // 验证License 若不验证则转化出的pdf文档会有水印产生
            return false;
        }
        FileOutputStream os = null;
        try {
            long old = System.currentTimeMillis();
            File file = new File(outPath); // 新建一个空白pdf文档
            os = new FileOutputStream(file);
            Document doc = new Document(inPath); // Address是将要被转化的word文档
//            doc.set
            doc.save(os, SaveFormat.PDF);// 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF,
            // EPUB, XPS, SWF 相互转换
            long now = System.currentTimeMillis();
            System.out.println("pdf转换成功,共耗时:" + ((now - old) / 1000.0) + "秒"); // 转化用时
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }finally {
            if (os != null) {
                try {
                    os.flush();
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    public static void main(String[] arg){

//        String docPath = "D:\\report\\word\\交通态势日报-2021-01-10.docx";
//        String pdfPath = "D:\\report\\word\\交通态势日报-2021-01-10.pdf";
//        Word2PdfAsposeUtil.doc2pdf(docPath,pdfPath);
//        UUID randomUUID = UUID.randomUUID();
//        String replaceAll = randomUUID.toString().replaceAll("-", "");
//        System.out.println("======> "+replaceAll);
        long timeMillis = System.currentTimeMillis();
        System.out.println("===> "+timeMillis);
    }
}

4.编写导出工具方法(可选导出pdf/docx)

import com.aspose.words.Document;
import com.aspose.words.SaveFormat;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.util.PoitlIOUtils;
import com.wlj.util.Word2PdfAsposeUtil;
import lombok.AllArgsConstructor;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
    /**
     * 导出
     * @param response 
     * @param fileName 模板名
     * @param map 数据map
     * @param type 导出类型
     * @param config 配置插件策略
     */
    public static void exportFile(
            HttpServletResponse response,
            String fileName,
            Map<String, Object> map,
            String type,
            Configure config) {
        try {
            // 获取Word模板,模板存放路径在项目的resources目录下
            //Kit就是方法所在的类
            InputStream ins = UserInfoServiceImpl.class.getResourceAsStream("/template/" + fileName);
            //文件在上方导包资源内
            InputStream xmlIn = UserInfoServiceImpl.class.getResourceAsStream("/license/license.xml");
            // 破解spiredoc水印
            Word2PdfAsposeUtil.getLicense(xmlIn);
            //若没有策略需求调用compile(InputStream)的重载方法即可
            XWPFTemplate template = XWPFTemplate.compile(ins, config).render(map);
            // 浏览器端下载
            response.setCharacterEncoding("utf-8");
            if (WORD.equals(type)) {
                response.setHeader("Content-disposition", "attachment;filename=\"" + fileName + "\"");
                response.setContentType("application/msword");
                OutputStream out = response.getOutputStream();
                BufferedOutputStream bos = new BufferedOutputStream(out);
                template.write(bos);
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out);
            } else if (PDF.equals(type)) {
                response.setHeader(
                        "Content-Disposition",
                        "attachment;filename=".concat(java.lang.String.valueOf(URLEncoder.encode("导出实例.pdf", "UTF-8"))));
                OutputStream out = response.getOutputStream();
                response.setContentType("application/pdf");
                //获取相对路径url
                URL url = UserInfoServiceImpl.class.getResource("/template/");
                template.writeAndClose(new FileOutputStream(url.getPath() + "temporary.docx"));
                InputStream in = UserInfoServiceImpl.class.getResourceAsStream("/template/temporary.docx");
                Document document = new Document(in);
                document.save(out, SaveFormat.PDF);
                IOUtils.copy(in, out);
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, ins, in, out);
                new File(url.getPath() + "temporary.docx").delete();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

封装占位符数据

Map<String, Object> resultMap = new HashMap<>();
//纯文本占位符
put("title", "Hi, poi-tl Word模板引擎");
//图片
// 指定图片路径
put("image", "/ce/logo.png");

// 设置图片宽高
put("image1", Pictures.ofLocal("logo.png").size(120, 120).create());

// 图片流
put("streamImg", Pictures.ofStream(new FileInputStream("logo.jpeg"), PictureType.JPEG)
  .size(100, 120).create());

// 网络图片(注意网络耗时对系统可能的性能影响)
put("urlImg", Pictures.ofUrl("http://deepoove.com/images/icecream.png")
  .size(100, 100).create());

// svg图片
put("svg", "https://img.shields.io/badge/jdk-1.6%2B-orange.svg");

// java图片
put("buffered", Pictures.ofBufferedImage(bufferImage, PictureType.PNG)
  .size(100, 100).create());
//如果是循环(区块对标签{{?xxx}}{{/xxx}})
//要循环的集合
List<Map<String, Object>> list = new ArrayList();
for (int i = 0; i < 3; i++) {
//循环添加要循环的数据
     Map<String, Object> map = new HashMap<>();
     map.put("code", Pictures.ofBufferedImage(bufferImage, PictureType.PNG)
                    .size(100, 100).create());
      list.add(map);
}
put("list", list);

导出表格(无表格模板使用table占位符)
在这里插入图片描述

编写直接输出表格数据方法

List<Map<String, Object>> l = new ArrayList<>();
List<对象> gxTypeModelVoList = 对象;
for (int i = 0; i < gxTypeModelVoList.size(); i++) {
     对象 vo = 对象内集合;
     Map<String, Object> m = new HashMap();
     m.put("title", (i + 1) + "." + vo.getType());
     RowRenderData row0 = Rows.of("序号", "内容", "步骤", "√")
                                    .textColor("000000")
                                    .bgColor("FFFFFF")
                                    .center()
                                    .create();
     TableRenderData tableRenderData = new TableRenderData();
     tableRenderData.addRow(row0);
     TableStyle ttt = new TableStyle();
     ttt.setColWidths(new int[] {700, 1300, 5500, 300});
     // 设置表格宽度
     ttt.setWidth("8000");
     // 设置表格居中对齐
     ttt.setAlign(TableRowAlign.LEFT);
     tableRenderData.setTableStyle(ttt);
     List<GxVo> gxListVo = gxTypeModelVoList.get(i).getGxListVo();
     	//封装循环行数据,与上面表头对应,是什么类型就添加对应对象,表格行就展示什么类型(图片、文本...)
         RowRenderData ffffff = Rows.of(
         j + 1 + "",
         对象.getName(),
         //替换换行
         对象.getTechnology().replaceAll("\\\\n", "\n").replaceAll("<br/>", "\n"),
                                                            str)
         .textColor("000000")
         .bgColor("FFFFFF")
         .textFontSize(8)
         .create();
         tableRenderData.addRow(ffffff);
              }
                  }
                    }
         List<RowRenderData> rows = tableRenderData.getRows();
         MergeCellRule.MergeCellRuleBuilder builder = MergeCellRule.builder();
         // 初始化合并起始行和结束行
         int cell0start = 0;
         int cell1start = 0;
         int cell0end = 0;
         int cell1end = 0;
         // 每一行
         for (int row = 0; row < rows.size(); row++) {
         // 最后一行跨过去
         if (row == rows.size() - 1) {
            continue;
            }
         // 合并规则
         RowRenderData rowRenderData0 = rows.get(row);
         RowRenderData rowRenderData1 = rows.get(row + 1);
         String row0Cell0 =  rowRenderData0.getCells().get(0).getParagraphs().get(0).getContents().toString();
         String row0Cell1 =  rowRenderData0.getCells().get(1).getParagraphs().get(0).getContents().toString();
         String row1Cell0 =  rowRenderData1.getCells().get(0).getParagraphs().get(0).getContents().toString();
         String row1Cell1 =  rowRenderData1.getCells().get(1).getParagraphs().get(0).getContents().toString();
          // 相等递加,直到不相等时合并
          if (row0Cell0.equals(row1Cell0)) {
                cell0end++;
          } else {
                Integer newStart = cell0start;
                Integer newEnd = cell0end;
                cell0start = row + 1;
                cell0end = row + 1;
                if (!newStart.equals(newEnd)) {
                    builder.map(MergeCellRule.Grid.of(newStart, 0), MergeCellRule.Grid.of(newEnd, 0));
                                }
                            }
                if (row0Cell1.equals(row1Cell1)) {
                     cell1end++;
                } else {
                     Integer newStart = cell1start;
                     Integer newEnd = cell1end;
                     cell1start = row + 1;
                     cell1end = row + 1;
               if (!newStart.equals(newEnd)) {
                     builder.map(MergeCellRule.Grid.of(newStart, 1), MergeCellRule.Grid.of(newEnd, 1));
                     }
                    }
                 }
               MergeCellRule rule = builder.build();
               tableRenderData.setMergeRule(rule);
               m.put("table", tableRenderData);
               l.add(m);
                 }
               resultMap.put("sections", l);

表格行循环插件

    public void exportUserInfo(HttpServletResponse response,String type) {
        List<UserInfo> list = list(wrapper);
        List<Map<String, Object>> r = new ArrayList<>();
        for (SysBaseUserInfo userInfo : list) {
            Map<String, Object> m = new HashMap<>();
            m.put("username", userInfo.getUsername());
            m.put("name", userInfo.getName());
            m.put("code", userInfo.getCode());
            m.put("phone", userInfo.getPhone());
            String avatar = userInfo.getAvatar();
            try {
                String[] split = avatar.split(",");
                List<PictureRenderData> imgList = new ArrayList<>();
                for (String s : split) {
                    InputStream in= 根据文件名获取文件流;
                    imgList.add(Pictures.ofStream(in, PictureType.JPEG)
                            .size(50, 50).create());
                }

                m.put("imgs", imgList);
            } catch (Exception e) {

            }
            r.add(m);
        }
        Map<String, Object> rm = new HashMap<>();
        rm.put("list", r);
        //使用行循环插件
        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        //绑定插件所属名为list
        Configure config = Configure.builder()
                .bind("list", policy).build();
        exportFile(response, "userInfo.docx", rm, type, config);
    }

在这里插入图片描述

也可在行循环中插入区块对标签实现循环该列集合,如上循环图片(只需有List集合即可,map键名此处为"imgs")

注:行循环需要用到LoopRowTableRenderPolicy插件,数据循环标签放于表头左上,循环行标签使用[占位符名]循环(注意行数据类型,如图片为[@标签名],也可省略标签名[@#this]),这里有个小问题就是如果循环图片行列表会换行展示,现行方法只能把图片尺寸调小,官方说在同一段落就不会换行,这个暂未研究。
与方法一相同,封装数据集合结构为:

  • 最外层Map集合,类型为:Map<String,Object>resultMap = new HashMap(),键为表头左上角占位符名,如上为list,值为下方循环行数据List集合
  • 循环行数据List集合,类型为:LIst<Map<String,Object>>list = new ArrayList()
  • 行数据Map<String,Object>rowMap = new HashMap(),键为行占位符名(如num等),值为具体的行数据,无需@等标签名

导出方法与上方相同
注:其实这些方法都从官方文档中能找到,怎么使用在业务需要,这里举一个项目经验例子以便以后学习使用

  • 5
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 28
    评论
poi-tl是一个基于POIJava模板引擎,可以用于导出Word文档。在poi-tl中,可以使用foreach指令实现循环,并根据需要循环输出。 在poi-tl中,可以将需要循环的数据存储在一个List对象中,然后使用foreach指令进循环遍历。假设我们有一个List<RowData>对象,其中RowData是自定义的类,用于表示每一的数据。RowData类中可以包含一些属性,代表每一中的不同。 首先,我们需要在Word模板中使用foreach指令,来循环输出。可以使用标签${foreach items=listVar item=rowVar}和${end}将需要循环的部分包围起来。其中listVar是存储数据的List对象的名称,rowVar是循环过程中每一数据的临时变量名。 然后,在foreach指令中,可以使用${rowVar.property}的方式获取每一的属性值,来完成对的输出。property代表RowData类中的某一个属性的名称。 最后,使用poi-tl提供的模板渲染引擎,将数据填充到Word模板中,生成目标Word文档。 综上所述,利用poi-tl可以很方便地循环输出。首先需要准备好存储数据的List对象,并在Word模板中使用foreach指令进循环遍历。然后,在循环过程中使用${rowVar.property}的方式获取每一的属性值,完成对的输出。最终,使用poi-tl提供的模板渲染引擎将数据填充到Word模板中,生成目标Word文档

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值