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

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

gradle:
//最新版poi-tl包含了poi的依赖可以不用导poi的依赖
implementation 'com.deepoove:poi-tl:最新版'
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也不一样,所以需要参考文档时要选择对应的版本文档,不然就会报一系列的异常。

配置结构
在这里插入图片描述

2.创建模板
模板样式任意(如下图),只需填充占位符即可,循环图片也可以使用{{?list}}{{@#this}} {{/list}}代码少写个Map
poi-tl导出复杂word/pdf文档(多张图片、表格循环行合并列、表格带图片、循环多个模板导出,多个文档合并)_第1张图片

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);
            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占位符)
poi-tl导出复杂word/pdf文档(多张图片、表格循环行合并列、表格带图片、循环多个模板导出,多个文档合并)_第2张图片

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

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("
"
, "\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集合,类型为:MapresultMap = new HashMap(),键为表头左上角占位符名,如上为list,值为下方循环行数据List集合
  • 循环行数据List集合,类型为:LIst>list = new ArrayList()
  • 行数据MaprowMap = new HashMap(),键为行占位符名(如num等),值为具体的行数据,无需@等标签名

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

你可能感兴趣的:(poi-tl导出,java,poi)