springboot将模板生成pdf文件

需求:

将后台的一堆数据,利用velocity模板将html格式生成pdf文件展示,通常在生成XX报告、XX公告场景下常用,毕竟pdf样式稳定,又可以下载。

velocity语法也特别简单,不会的可以看下https://blog.csdn.net/qiaodaima0/article/details/126158419?spm=1001.2014.3001.5501

思路:

将文字内容生成pdf文件用itextpdf就可以很简单的完成,但是pdf一般要求特定的格式(比如报告类的就喜欢用固定的红头标题)或者一定的美观性,纯用后端渲染就不是很方便了,但是html渲染样式就很简单,两个结合就能比较简单生成美观的pdf。

环境:

springboot 2.7.2
jdk1.8
velocity 1.7
itextpdf

实操:

包目录
在这里插入图片描述

1、让前端小姐姐搞一个漂亮的html

注意:html里面的图片或js/css等其他的静态资源不允许用外部连接,图片就直接用base64码即可
我们弄一个简单带表格的html,如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
</head>
<body>
<div class="contianer">
    <div class="bg-img-container">
    </div>
    <h1>模板pdf</h1>
    <div class="flex-div">
        <p>表1</p>
    </div>

    <table border="1" cellSpacing="1">
        <tr>
            <th>姓名</th>
            <td colspan="3">$!{name}</td>
        </tr>
        <tr>
            <th #if($!{gender}==1) class="blue" #else class="red" #end>性别</th>
            <td colspan="3">
                #if($!{gender}==1)
                男
                #else
                女
                #end
            </td>
        </tr>
        <tr>
            <th>出生日期</th>
            <td>$!{birthDate}</td>
            <th>联系方式</th>
            <td>$!{phone}</td>
        </tr>
    </table>
    <h1>双重For循环取值</h1>
    <div class="flex-div">
        <p>表2</p>
    </div>
    <table border="1" cellSpacing="1">
        <tr>
            <th>学历</th>
            <th>学科</th>
            <td class="gray-bg">分数</td>
        </tr>
        #foreach($item in $eduList)
        <tr>
            <th  #set($len= $item.size+1) rowspan="$len">$item.name</th>
            #foreach($result in $item.itemList)
        <tr>
            <td>$velocityCount、$result.subject</td>
            <td #if($result.num > 60) class="green" #else class="red" #end>$result.num</td>
        </tr>
        #end
        </tr>
        #end
    </table>

</div>

</body>
<style>
    .blue {
        color: #244385;
        margin-left: 30px;
    }

    .green {
        color: #52c41a;
        margin-left: 30px;
    }

    .red {
        color: #c1181e;
        margin-left: 30px;
    }

    .flex-div {
        margin: 30px 0 15px;
        display: flex;
        align-items: center;
    }

    .line {
        width: 540px;
        height: 0px;
        border: dashed 1px #edf0f5;
    }

    table {
        width: 700px;
        border-collapse: collapse;
        /*border-spacing: 0;*/
        border-left: 1px solid #edf0f5;
        border-top: 1px solid #edf0f5;
    }

    th, td {
        border-right: 1px solid #edf0f5;
        border-bottom: 1px solid #edf0f5;
        padding: 5px 15px;
    }

    h1 {
        text-align: center;
        font-family: 'Microsoft YaHei';
        line-height: 60px;
        letter-spacing: 8px;
        color: #244385;
    }

    p {
        font-family: 'Microsoft YaHei';
        font-size: 20px;
        font-weight: normal;
        color: #244385;
        margin: 0 16px;
    }

    th {
        background-color: rgba(0, 0, 0, 0.01);
        width: 90px;
        min-width: 90px;
        max-width: 90px;
        text-align: left;
        text-indent: 10px;
        font-family: 'Microsoft YaHei';
        font-size: 16px;
        font-weight: normal;
        font-stretch: normal;
        line-height: 48px;
        letter-spacing: 2px;
        color: #666666;
    }

    td {
        font-size: 16px;
        color: #000000;
        text-align: left;
        text-indent: 10px;
        padding-right: 20px;
        height: 50px;
    }

    table,tr, th, td {
        border: solid 1px #edf0f5;
    }

    .gray-bg {
        background-color: rgba(0, 0, 0, 0.01);
    }
</style>
</html>

2、pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.template.pdf</groupId>
    <artifactId>template_pdf</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--工具类依赖-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.19</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

        <!--pdf-->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.7</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>html2pdf</artifactId>
            <version>4.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>font-asian</artifactId>
            <version>7.2.3</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3、PdfUtil工具类

import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
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 org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;

/**
 * PDF工具
 *
 * @author ppp
 * @date 2022/8/5
 */
public class PdfUtil {

    static {
        // Velocity初始化
        Velocity.setProperty(RuntimeConstants.OUTPUT_ENCODING, StandardCharsets.UTF_8);
        Velocity.setProperty(RuntimeConstants.INPUT_ENCODING, StandardCharsets.UTF_8);
        Velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
        Velocity.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        Velocity.init();
    }


    /**
     * 据模板生成pfd格式文件
     *
     * @param context      上下文对象
     * @param template     pdf模板
     * @param outputStream 生成ofd文件输出流
     */
    public static void pdfFile(Context context, String template, OutputStream outputStream) {
        try (PdfWriter pdfWriter = new PdfWriter(outputStream)) {
            PdfDocument pdfDocument = new PdfDocument(pdfWriter);
            pdfDocument.setDefaultPageSize(PageSize.A4);

            ConverterProperties properties = new ConverterProperties();
            FontProvider fontProvider = new FontProvider();
            // 字体设置,解决中文不显示问题
            PdfFont sysFont = PdfFontFactory.createFont("STSongStd-Light", "UniGB-UCS2-H");
            fontProvider.addFont(sysFont.getFontProgram(), "UniGB-UCS2-H");
            properties.setFontProvider(fontProvider);

            Template pfdTemplate = Velocity.getTemplate(template, "UTF-8");
            StringWriter writer = new StringWriter();
            pfdTemplate.merge(context, writer);
            HtmlConverter.convertToPdf(writer.toString(), pdfDocument, properties);
            pdfDocument.close();
        } catch (Exception e) {
            throw new RuntimeException("PFD文件生成失败", e);
        }
    }

}

4、Controller接口测试

/**
* PDF生成Controller
* @author ppp
* @date 2022/8/4
*/
@Controller
@RequestMapping("/velocity")
public class PdfController {

    @RequestMapping("/getPdf")
    public void get(HttpServletResponse response){
        response.reset();
        response.setContentType("application/pdf");
        String filename = System.currentTimeMillis()+".pdf";
        response.addHeader("Content-Disposition", "inline; filename=" + URLUtil.encode(filename, CharsetUtil.CHARSET_UTF_8));
        VelocityContext context = new VelocityContext();

        context.put("name", "彭也行");
        context.put("gender", 1);
        context.put("birthDate", DateUtil.formatDateTime(new Date()));
        context.put("phone", "13666666666");

        List<Map<String, Object>> eduList = new ArrayList<>();
        // 小学
        Map<String, Object> primarySchoolMap = new HashMap<>();
        primarySchoolMap.put("name", "小学");
        List<Map<String, Object>> scoreList = new ArrayList<>();
        // 语文成绩
        Map<String, Object> chineseScore = new HashMap<>();
        chineseScore.put("subject", "语文");
        chineseScore.put("num", 60);
        // 数学成绩
        Map<String, Object> mathScore = new HashMap<>();
        mathScore.put("subject", "数学");
        mathScore.put("num", 99);
        scoreList.add(chineseScore);
        scoreList.add(mathScore);
        primarySchoolMap.put("itemList", scoreList);
        primarySchoolMap.put("size", scoreList.size());

        // 初中
        Map<String, Object> middleSchoolMap = new HashMap<>();
        middleSchoolMap.put("name", "初中");
        List<Map<String, Object>> middleScoreList = new ArrayList<>();
        // 语文成绩
        Map<String, Object> middleChineseScore = new HashMap<>();
        middleChineseScore.put("subject", "语文");
        middleChineseScore.put("num", 60);
        // 数学成绩
        Map<String, Object> middleMathScore = new HashMap<>();
        middleMathScore.put("subject", "数学");
        middleMathScore.put("num", 99);
        // 英语
        Map<String, Object> middleEnScore = new HashMap<>();
        middleEnScore.put("subject", "英语");
        middleEnScore.put("num", 55);

        middleScoreList.add(middleChineseScore);
        middleScoreList.add(middleMathScore);
        middleScoreList.add(middleEnScore);
        middleScoreList.add(middleEnScore);
        middleScoreList.add(middleEnScore);
        middleScoreList.add(middleEnScore);
        middleScoreList.add(middleEnScore);
        middleScoreList.add(middleEnScore);
        middleSchoolMap.put("itemList", middleScoreList);
        middleSchoolMap.put("size", middleScoreList.size());

        eduList.add(primarySchoolMap);
        eduList.add(middleSchoolMap);
        context.put("eduList", eduList);
        try(ServletOutputStream outputStream = response.getOutputStream()){
            PdfUtil.pdfFile(context, "demo.html", outputStream);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

5、效果

还是很好看的,用户访问连接就可以直接预览,也可以自己下载
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6、如果不想预览,直接转换

在这里插入图片描述

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值