java使用freemarker通过模板文件导出pdf文件

前言:

​ 尝试了不少通过模板文件导出pdf文件的方法,要么实现起来复杂,要么实现效果不理想,经过反复查找资料发现此方法是最理想的。

一,依赖jar包
<!-- freemarker 读取html模板文件 -->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.23</version>
</dependency>
<!-- xml 将html模板文件转换成pdf -->
<dependency>    
    <groupId>org.xhtmlrenderer</groupId>
    <artifactId>flying-saucer-pdf</artifactId>
    <version>9.0.9</version>
</dependency>
二,模板文件(为html,命名后缀为.ftl)
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8"/>
		<title></title>
		<style>
			*{
				margin: 0;
				padding: 0;
				box-sizing: border-box;
			}
			body{
				font-family: SimSun;
			}
			section{
				display:block;
				margin: 20px 10px;
			}
			.title{
				text-align: center;
			}
			.preface p{
				line-height: 30px;
			}
			.preface p.content{
				text-indent: 2em;
			}
			section > table{
				table-layout: fixed;
				width: 100%;
				margin: 20px 0px;
				text-align:center;
				word-wrap:break-word;
			}
			section table td{
				padding:5px 0px;
			}
		</style>
	</head>
	<body>
		<!-- 标题 start -->
		<section class="title">
			<h2>某报告</h2>
		</section>
		<!-- 标题 end -->
		
		<!-- 前言 start -->
		<section class="preface">
			<p>尊敬的用户:</p>
			<p class="content">内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p>
		</section>
		<!-- 前言 end -->
		
		<!-- 汇总统计信息 start -->
		<section class="count-info">
			<h4>汇总统计信息</h4>
			<table border="1" cellspacing="0" cellpadding="0">
				<tr>
					<td>本月笔数</td>
					<td>近三个月数量对比</td>
				</tr>
				<tr>
					<td>${curr}</td>
					<td>
						<table width="80%" border="1" cellspacing="0" cellpadding="0" style="margin: 5px auto;">
							<tr>
								<td>${one}</td>
								<td>${two}</td>
								<td>${three}</td>
							</tr>
						</table>
					</td>
				</tr>
			</table>
		</section>
		<!-- 汇总统计信息 end -->
		
		<!-- 明细 start -->
		<section class="detail">
			<h4>明细</h4>
			<table border="1" cellspacing="0" cellpadding="0">
				<tr>
					<td width="5%">序号</td>
					<td width="15%">列1</td>
					<td width="12%">列2</td>
					<td width="12%">列3</td>
					<td width="12%">列4</td>
					<td>列5</td>
				</tr>
				<#list detailList as ad>
					<tr>
						<td>${ad_index+1}</td>
						<td>${ad.column1}</td>
						<td>${ad.column2}</td>
						<td>${ad.column3}</td>
						<td>${ad.column4}</td>
						<td>${ad.column5}</td>
					</tr>
				</#list>
			</table>
		</section>
		<!-- 明细 end -->
	</body>
</html>

在这里插入图片描述

三,工具类实现
package test.pdf;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Locale;
import java.util.Map;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.lowagie.text.pdf.BaseFont;
import freemarker.template.Configuration;
import freemarker.template.Template;

public class PDFTemplateUtil {

	/**
	 * 通过模板导出pdf文件
	 * @param data 数据
	 * @param templateFileName 模板文件名
	 * @throws Exception
	 */
    public static ByteArrayOutputStream createPDF(Map<String,Object> data, String templateFileName) throws Exception {
        // 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
        Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 指定FreeMarker模板文件的位置 
        cfg.setClassForTemplateLoading(PDFTemplateUtil.class,"/templates");
        ITextRenderer renderer = new ITextRenderer();
        OutputStream out = new ByteArrayOutputStream();
        try {
            // 设置 css中 的字体样式(暂时仅支持宋体和黑体) 必须,不然中文不显示
            renderer.getFontResolver().addFont("/templates/font/SIMSUN.TTC", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            // 设置模板的编码格式
            cfg.setEncoding(Locale.CHINA, "UTF-8");
            // 获取模板文件 
            Template template = cfg.getTemplate(templateFileName, "UTF-8");
            StringWriter writer = new StringWriter();
            
            // 将数据输出到html中
            template.process(data, writer);
            writer.flush();

            String html = writer.toString();
            // 把html代码传入渲染器中
            renderer.setDocumentFromString(html);

            // 解决图片的相对路径问题 ##必须在设置document后再设置图片路径,不然不起作用
            renderer.getSharedContext().setBaseURL("images/");
            renderer.layout();
            
            renderer.createPDF(out, false);
            renderer.finishPDF();
            out.flush();
            return (ByteArrayOutputStream)out;
        } finally {
        	if(out != null){
        		 out.close();
        	}
        }
    }
}

注:SIMSUN.TTC为宋体,可在C:\Windows\Fonts中查找

四,控制类实现
package test.controller;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import test.domain.PDFDataTest;
import test.pdf.PDFTemplateUtil;

@RestController
@RequestMapping("/pdf")
public class PdfController {
	
	@RequestMapping("/export")
	public void exportPdf(HttpServletResponse response) throws Exception{
		ByteArrayOutputStream baos = null;
		OutputStream out = null;
		try {
			// 模板中的数据,实际运用从数据库中查询
			Map<String,Object> data = new HashMap<>();
			data.put("curr", 1);
			data.put("one", 2);
			data.put("two", 1);
			data.put("three", 6);
			
			List<PDFDataTest> detailList = new ArrayList<>();
			detailList.add(new PDFDataTest(123456,"测试","测试","测试","测试"));
			detailList.add(new PDFDataTest(111111,"测试","测试","测试","测试"));
			detailList.add(new PDFDataTest(222222,"测试","测试","测试","测试"));
			data.put("detailList", detailList);
			
			baos = PDFTemplateUtil.createPDF(data, "pdf测试模板.ftl");;
			// 设置响应消息头,告诉浏览器当前响应是一个下载文件
			response.setContentType( "application/x-msdownload");
			// 告诉浏览器,当前响应数据要求用户干预保存到文件中,以及文件名是什么 如果文件名有中文,必须URL编码 
			String fileName = URLEncoder.encode("月度报告.pdf", "UTF-8"); 
			response.setHeader( "Content-Disposition", "attachment;filename=" + fileName);
			out = response.getOutputStream();
			baos.writeTo(out);
			baos.close();
		} catch (Exception e) {
			e.printStackTrace();
		    throw new Exception("导出失败:" + e.getMessage());
		} finally{
			if(baos != null){
				baos.close();
			}
			if(out != null){
				out.close();
			}
		}
	}
}

注意:模板中占位符值不能为null

附:PDFDataTest类

package test.domain;

public class PDFDataTest {
	
	private Integer column1;
	
	private String column2;
	
	private String column3;
	
	private String column4;
	
	private String column5;
	
	public PDFDataTest(Integer column1, String column2, String column3, String column4, String column5) {
		super();
		this.column1 = column1;
		this.column2 = column2;
		this.column3 = column3;
		this.column4 = column4;
		this.column5 = column5;
	}

	public Integer getColumn1() {
		return column1;
	}

	public void setColumn1(Integer column1) {
		this.column1 = column1;
	}

	public String getColumn2() {
		return column2;
	}

	public void setColumn2(String column2) {
		this.column2 = column2;
	}

	public String getColumn3() {
		return column3;
	}

	public void setColumn3(String column3) {
		this.column3 = column3;
	}

	public String getColumn4() {
		return column4;
	}

	public void setColumn4(String column4) {
		this.column4 = column4;
	}

	public String getColumn5() {
		return column5;
	}

	public void setColumn5(String column5) {
		this.column5 = column5;
	}
	
}
五、实现效果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_39806100/article/details/86616041
今日推荐