Spring - Boot Thymeleaf生成转为PDF并保存

Spring - Boot Thymeleaf生成转为PDF并保存

一,需要引入的依赖包

            <!--     HTML转pdf       -->
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>html2pdf</artifactId>
                <version>4.0.4</version>
            </dependency>

            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>font-asian</artifactId>
                <version>7.2.0</version>
            </dependency>

            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>commons</artifactId>
                <version>7.2.0</version>
            </dependency>
            <dependency>
                <groupId>com.itextpdf</groupId>
                <artifactId>kernel</artifactId>
                <version>7.2.0</version>
            </dependency>

            <dependency>
                <groupId>ognl</groupId>
                <artifactId>ognl</artifactId>
                <version>3.2.6</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
            <dependency>
                <groupId>org.jsoup</groupId>
                <artifactId>jsoup</artifactId>
                <version>1.14.2</version>
            </dependency>

            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.3.20</version>
            </dependency>
            <!-- 这个地方需要特别注意 itextpdf 的包除了html2pdf以外 其他的包必须统一版本  否则会出现依赖冲突问题  导致执行程序出错  -->

创建thymeleaf页面

这个地方就不多说了 可以去百度看下thymeleaf的基本语法


<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8">
    <title>测试</title>
    <style>
        .bill-print-root {
            height: 100%;
            overflow: auto;
        }
        .bill-print-page {
            background-color: #fff;
            min-height: 100%;
            overflow: auto;
            overflow-x: hidden;
            font-size: 12px;
            padding: 0 10px;
        }
        .bill-name {
            text-align: center;
            font-size: 24px;
            padding-top: 20px;
        }
        .el-button {
            float: right;
        }
        .area {
            margin-top: 10px;
            border: 2px solid #b5b5b5!important;
            border-collapse: collapse;
            border-spacing: 0;
        }
        thead {
            font-weight: 700;
        }
        .head {
            margin-top: 5px;
            border: 2px solid #b5b5b5!important;
            border-collapse: collapse;
            border-spacing: 0;
        }
        th {
            padding: 10px 5px;
            border: 1px solid #b5b5b5;
            vertical-align: middle;
            text-align: center!important;
        }
        td {
            padding: 10px 5px;
            border: 1px solid #b5b5b5;
            vertical-align: middle;
            text-align: center!important;
        }
        .spanText {
            word-break: break-all;
        }
    </style>
</head>
<body>
<div class="bill-print-root">
    <div>
        <div>
            <div class="bill-print-page">
                <div style="margin-bottom: 50px;">
                    <h1 class="bill-name" th:text="${printInfoBO.title}"></h1>
                    <div class="page">
                        <table width="100%" class="head">
                            <thead>
                            <tr>
                                <th colspan="6"><span>测试</span></th>
                            </tr>
                            </thead>
                            <tbody>
                            <tr th:each="fieldList:${headList}">
                                <th>
                                    <span th:text="${fieldList.get(0).colName}"></span>
                                </th>
                                <td>
                                    <span th:if="${printInfoBO.data.XXXXX}"
                                          th:text="${printInfoBO.data.XXXX}"></span>
                                </td>
                                <th th:if="${fieldList.size()%3 ne 1}">
                                    <span th:text="${printInfoBO.data.XXXX}"></span>
                                </th>
                                <td th:if="${fieldList.size()%3 ne 1}">
                                    <span  th:if="${printInfoBO.data.XXXX}"
                                           th:text="${printInfoBO.data.XXXX}"></span>
                                </td>
                                <th th:if="${fieldList.size()%3 eq 0}">
                                    <span th:text="${fieldList.get(2).colName}"></span>
                                </th>
                                <td th:if="${fieldList.size()%3 eq 0}">
                                    <span th:if="${printInfoBO.data.XXXX}"
                                          th:text="${printInfoBO.data.XXXX}"></span>
                                </td>
                            </tr></tbody>
                        </table>
                        <div class="body">
                            <div>
                                <table width="100%" class="area" th:each="bodyData:${printInfoBO.body}">
                                    <thead>
                                    <tr>
                                        <td th:colspan="${bodyData.fieldList.size()}" th:text="${printInfoBO.data.XXXX}"></td>
                                    </tr>
                                    <tr>
                                        <td th:each="field:${bodyData.fieldList}"><span th:text="${field.colName}"></span></td>
                                    </tr>
                                    </thead>
                                    <tbody>
                                    <tr th:each="${printInfoBO.data.XXXX}">
                                        <td th:each="field:${bodyData.fieldList}">
                                            <span class="spanText" th:if="${data.get(field.colCode)}"
                                                  th:text="${data.get(field.colCode).get('LABEL')}"></span>
                                        </td>
                                    </tr>
                                    </tbody>
                                </table>
                            </div>
                            <div>
                                <table width="100%" class="area">
                                    <thead>
                                    <tr>
                                        <td colspan="5">审批历史</td>
                                    </tr>
                                    <tr>
                                        <td width="20%"><span>审批环节</span></td>
                                        <td width="20%"><span>审批人</span></td>
                                        <td width="20%"><span>审批状态</span></td>
                                        <td width="20%"><span>审批意见</span></td>
                                        <td width="20%"><span>审批时间</span></td>
                                    </tr>
                                    </thead>
                                    <tbody>
                                    <tr th:each="${printInfoBO.data.XXXX}">
                                        <td><span th:text="${printInfoBO.data.XXXX}"></span></td>
                                        <td><span th:text="${printInfoBO.data.XXXX}"></span></td>
                                        <td><span th:text="${printInfoBO.data.XXXX}"></span></td>
                                        <td><span th:text="${printInfoBO.data.XXXX}"></span></td>
                                        <td><span th:text="${printInfoBO.data.XXXX}"></span></td>
                                    </tr>
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

三,传入参数生成页面转为String格式的Html原生代码

package com.stream.ecs.bill.listener.normal.complateEnd;

import com.stream.bill.manager.IBillFillManager;
import com.stream.core.client.FeignClientUtil;
import com.stream.core.util.BeanUtil;
import com.stream.core.util.ListUtil;
import com.stream.core.util.StringUtil;
import com.stream.ecs.bill.event.normal.EcsBillComplateEndEvent;
import com.stream.ecs.bill.util.ITextHtmlPdfUtil;
import com.stream.pub.bo.bill.print.QueryBillPrintInfoBO;
import com.stream.pub.bo.fill.AttachmentMergeBO;
import com.stream.pub.bo.fill.FileAttachment;
import com.stream.pub.bo.fill.FileAttachmentMergeReqBO;
import com.stream.pub.client.file.IFileAttachmentClient;
import com.stream.pub.client.storage.IStorageClient;
import com.stream.pub.enums.file.FileBusinessTypeEnum;
import org.apache.http.entity.ContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 
 * @author x
 */
@Component
public class CreatePDFOnCompleteEndEventListener  {

    @Autowired
    private IXXXXXXXXManager XXXXXXXManager;

    @Autowired
    private SpringTemplateEngine templateEngine;
    /**
     * 生成单据PDF保存到镜像服务器
     * @param event
     */
    @Override
    public void onApplicationEvent() {

        MultipartFile fileResult;
        /* 获取模板信息 */
        List<QueryBillPrintInfoBO<Map<String, Object>>> queryBillPrintInfoBOS = billFillManager.queryBillPrintInfo(event.getBillInfo().getModuleCode(), mainIds, event.getUserInfo());
        QueryBillPrintInfoBO<Map<String, Object>> printInfoBO = queryBillPrintInfoBOS.get(0);
        /* 保存进map */
        Map<String, Object> params = new HashMap();
        params.put("printInfoBO", printInfoBO);
        params.put("headList", ListUtil.split(printInfoBO.getHead().getFieldList(),3));
        /* 创建上下文对象 */
        Context context = new Context();
        context.setVariables(params);
        /* 构建生成html页面 返回字符串内容 */
        String process = templateEngine.process("test.html", context);
        System.out.println(process);

        /* 生成pdf文件并获取字节数组 */
        try {
            byte[] bytes = ITextHtmlPdfUtil.createPdf(process);
            /* 转换为MultipartFile文件 */
            InputStream inputStream = new ByteArrayInputStream(bytes);
            fileResult = new MockMultipartFile(event.getBillMain().getBillNo() + "-打印件.pdf",event.getBillMain().getBillNo() + "-打印件.pdf" ,ContentType.WILDCARD.toString(),inputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        List<MultipartFile> fileList = new ArrayList<>();
        fileList.add(fileResult);
        /* 存储 */
        List<AttachmentMergeBO> mergeBOS = FeignClientUtil.target(IStorageClient.class).batchUpload(fileList);
        /* 存储附件信息 */
    }

}

四,这是HTML生成PDF的核心工具类

package com.stream.ecs.bill.util;

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider;
import com.itextpdf.io.font.FontProgram;
import com.itextpdf.io.font.FontProgramFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.styledxmlparser.css.media.MediaDeviceDescription;
import com.itextpdf.styledxmlparser.css.media.MediaType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * jzc
 */
@Slf4j
public class ITextHtmlPdfUtil {

    /**
     * html路径
     */
    public static final String ANALYSE_HTML = "/html/pdfPage.html";

    /**
     * 中文支持插件在resource下的位置
     */
    public static final String FONT = "/fonts/fzsjt.TTF";

    /**
     * PDF扩展名
     */
    public static final String PDF_EXTENSION = ".pdf";

    public static final String ANALYSE_REPORT = "前缀";

    /**
     * pdf html 的路径
     */
    public static final String TEMPLATE_PATH = "/template";

    public static byte[] createPdf(String htmlString) throws IOException {

        PageSize pageSize = new PageSize(1200, 2000);

        return createPdf(TEMPLATE_PATH, htmlString, pageSize);
    }

    // html 转 pdf 并返回字节数组
    public static byte[] createPdf(String resourceTemplateUri, String html, PageSize pageSize) throws IOException {
        //设置pdf的尺寸
        ByteArrayOutputStream outputStream = null;
        InputStream fontInputStream = null;
        try {
            outputStream = new ByteArrayOutputStream();
            PdfWriter writer = new PdfWriter(outputStream);
            PdfDocument pdf = new PdfDocument(writer);
            pdf.setTagged();
            pdf.setDefaultPageSize(pageSize);

            ConverterProperties properties = new ConverterProperties();
            //pdf宽高
            properties.setBaseUri(resourceTemplateUri);
            MediaDeviceDescription mediaDeviceDescription = new MediaDeviceDescription(MediaType.SCREEN);
            mediaDeviceDescription.setWidth(pageSize.getWidth());
            properties.setMediaDeviceDescription(mediaDeviceDescription);
            //中文支持
            /** 这个地方特别注意 必须要在Resource项目目录下传入字体格式包 可以去搜下载 很好找 */
            FontProvider fontProvider = new DefaultFontProvider();
            fontInputStream = ITextHtmlPdfUtil.class.getResourceAsStream(FONT);
            FontProgram fontProgram = FontProgramFactory.createFont(IOUtils.toByteArray(fontInputStream));

            fontProvider.addFont(fontProgram);
            properties.setFontProvider(fontProvider);

            InputStream inputStreamRoute = new ByteArrayInputStream(html.getBytes());
            HtmlConverter.convertToPdf(inputStreamRoute, pdf, properties);

            return outputStream.toByteArray();
        } finally {
            IOUtils.closeQuietly(outputStream);
            IOUtils.closeQuietly(fontInputStream);
        }
    }
}

在这里插入图片描述
这是结构图

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值