java中freemarker使用ftl模版生成PDF文件

说明

调用方法生成PDF时,使用的ftl模版,以及字体都是从jar中读取的,无需担心多节点部署

引用jar

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker-gae</artifactId>
    <version>2.3.23</version>
</dependency>

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

文件存放路径

// 字体文件
resources/fonts
// 模版文件
resources/templates

代码

import com.lowagie.text.pdf.BaseFont;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.xhtmlrenderer.pdf.ITextRenderer;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Map;

@Component
public class PdfHelper {

    /**
     * classpath路径
     */
    private String classpath = getClass().getResource("/").getPath();

    /**
     * 指定FreeMarker模板文件的位置
     */
    private String templatePath = "/templates";

    /**
     * freeMarker模板文件名称
     */
    private String templateFileName = "pdf.ftl";

    /**
     * 图片路径 —— 默认是classpath下面的images文件夹
     */
    private String imagePath = "/images/";

    /**
     * 字体资源文件 存放路径
     */
    private String fontPath = "fonts/";

    /**
     * 字体   [宋体][simsun.ttc]   [黑体][simhei.ttf]
     */
    private String font = "simsun.ttc";

    /**
     * 指定编码
     */
    private String encoding = "UTF-8";

    @Value("{$file.path}")
    private String filePath;

    /**
     * 生成pdf
     *
     * @param data 传入到freemarker模板里的数据
     * @param out  生成的pdf文件流
     */
    public void createPDF(Map<String, Object> data, OutputStream out, String templateName) throws Exception {
        // 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
        Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 指定FreeMarker模板文件的位置
        ITextRenderer renderer = new ITextRenderer();
        String simsun = System.getProperty("user.dir") + "/simsun.ttc";
        String arialuni = System.getProperty("user.dir") + "/arialuni.ttf";
        File fondFile = new File(simsun);
        if (!fondFile.exists()) {
            getFondPath();
        }
        // 设置 css中 的字体样式(暂时仅支持宋体和黑体)
        renderer.getFontResolver().addFont(simsun, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        renderer.getFontResolver().addFont(arialuni, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

        StringTemplateLoader stringLoader = new StringTemplateLoader();
        String templateString = getFileData("templates/" + templateName);
        stringLoader.putTemplate("myTemplate", templateString);
        cfg.setTemplateLoader(stringLoader);
        Template tpl = cfg.getTemplate("myTemplate", "utf-8");
        StringWriter writer = new StringWriter();

        // 把null转换成""
        Map<String, Object> dataMap = CommunalTool.objectToMap(data);
        // 将数据输出到html中
        tpl.process(data, writer);
        writer.flush();

        String html = writer.toString();
        // 把html代码传入渲染器中
        renderer.setDocumentFromString(html);
       
        renderer.layout();
        renderer.createPDF(out, false);
        renderer.finishPDF();
        out.flush();
        out.close();

    }

    // 读取jar中的ftl模版文件
    private String getFileData(String path) {
        InputStream stream = getClass().getClassLoader().getResourceAsStream(path);
        // log.info("infile:"+infile);
        StringBuffer sb = new StringBuffer();
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
            String s = null;
            while ((s = br.readLine()) != null) {
                sb.append(s);
            }
            br.close();
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取字体路径
     *
     * @return
     * @throws IOException
     */
    private String getFondPath() throws IOException {
        String fontPath = System.getProperty("user.dir");
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream("fonts/simsun.ttc");
        //在根目录生成一个文件
        File targetFile = new File(fontPath + "/simsun.ttc");
        // //将流转成File格式
        FileUtils.copyInputStreamToFile(inputStream, targetFile);

        InputStream arialuniStream = getClass().getClassLoader().getResourceAsStream("fonts/arialuni.ttf");
        File arialuni = new File(fontPath + "/arialuni.ttf");
        FileUtils.copyInputStreamToFile(arialuniStream, arialuni);
        return fontPath;
    }

    public void setClasspath(String classpath) {
        this.classpath = classpath;
    }


    public void setTemplatePath(String templatePath) {
        this.templatePath = templatePath;
    }


    public void setTemplateFileName(String templateFileName) {
        this.templateFileName = templateFileName;
    }


    public void setImagePath(String imagePath) {
        this.imagePath = imagePath;
    }


    public void setFontPath(String fontPath) {
        this.fontPath = fontPath;
    }


    public void setFont(String font) {
        this.font = font;
    }


    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

}

工具类

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ValueFilter;
import org.apache.commons.lang3.StringUtils;
import sun.misc.BASE64Encoder;

import java.io.File;
import java.io.FileInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;


public class CommunalTool {


    /**
     * 转换数据中的null为""(空串)
     *
     * @param obj       转换之前的数据
     * @param beanClass 转换之后的数据类型
     * @return 返回Object
     * @throws Exception
     */
    public static Object objectToData(Object obj, Class<?> beanClass) throws Exception {
        return JSONObject.parseObject(objectToString(obj), beanClass);
    }

    /**
     * Map中的null转换成""(空串)
     *
     * @param map 转换之前的数据
     * @return 返回 Map<String,Object>
     * @throws Exception
     */
    public static Map<String, Object> objectToMap(Map<String, Object> map) throws Exception {
        return JSONObject.parseObject(objectToString(map), Map.class);
    }

    /**
     * 把list中的null转换成""(空串)
     *
     * @param list 转换之前的数据
     * @return 返回list
     * @throws Exception
     */
    public static List objectToList(List list) throws Exception {
        return JSONObject.parseObject(objectToString(list), List.class);
    }

    // fistJson 的一个过滤器
    static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    static DateFormat format = new SimpleDateFormat("yyyy-MM-dd");
    private static ValueFilter filter = new ValueFilter() {
        @Override
        public Object process(Object obj, String s, Object v) {
            if (v instanceof Date) {
                return simpleDateFormat.format(v);
            } else {
                if (v == null) {
                    return "";
                } else {
                    return v;
                }
            }
        }
    };

    /**
     * 将对象转换为json格式的字符串
     *
     * @param obj
     * @return String
     */
    public static String objectToString(Object obj) {
        // JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
        return JSON.toJSONString(obj, filter, SerializerFeature.WriteNonStringKeyAsString, SerializerFeature.WriteNullStringAsEmpty);
    }

    /**
     * 将图片转成base64 字符串
     *
     * @param path 文件路径
     * @return
     * @throws Exception
     */
    public static String encodeBase64Picture(String path) throws Exception {
        File file = new File(path);
        FileInputStream inputFile = new FileInputStream(file);
        byte[] buffer = new byte[(int) file.length()];
        inputFile.read(buffer);
        inputFile.close();
        String result = new BASE64Encoder().encode(buffer);
        if (StringUtils.isNotBlank(result)) {
            result = "data:image/png;base64," + result;
        } else {
            result = null;
        }
        return result;
    }

    /**
     * 时间转换  yyyy-MM-dd
     *
     * @param date
     * @return
     */
    public static String getTransformationDate(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(date);
    }

    /**
     * 时间转换  yyyy-MM-dd
     *
     * @param date
     * @return
     */
    public static String getTimeDate(Date date) {
        int minte = getMinute(date);
        minte = minte % 5;
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        Date afterDate = new Date(date.getTime() + 300000);
        System.out.println(sdf.format(afterDate));
        return "1";
    }

    /**
     * 功能描述:返回分
     *
     * @param date
     *            日期
     * @return 返回分钟
     */
    public static int getMinute(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.MINUTE);
    }

    /**
     * 获取日期年份
     * @param date 日期
     * @return
     */
    public static String FORMAT_FULL = "yyyy-MM-dd HH:mm:ss";
    public static String getTime(Date date) {
        SimpleDateFormat df = new SimpleDateFormat(FORMAT_FULL);
        return df.format(date).substring(12, 16);
    }

}


ftl模版

电子化模版单个表格
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta charset="UTF-8" />
	<title>模版</title>
	<style type="text/css">
		body{width:100%;}
		@media screen and (max-width: 768px) {
			body{font-size: 14px;}
			table{table-layout: fixed;}  }
		p{line-height: 26px;margin-right:15px;}
		table td p{line-height:6px;margin-right:0;padding-right:0;padding-left:0;}
		span{margin:0;padding:0;}
	</style>
	<style type="text/css" mcs_bogus="1">
		@page {
			size: a4 landscape;/*210*297*/
			/*size:195mm 271mm;*/
			margin: 8mm 8mm;
			padding: 0;
			/*border:solid #F00 1px;*/
			background-image:url(images/water.gif);
			background-repeat:no-repeat;
			background-position:center;
			@top-left {
				content: element(header);
			}
			@bottom-left {
				content: element(footer);
			}
			@bottom-center {
				content: "--- " counter(page) " ---" counter(main-heading);
			}
			@bottom-right {
				content: "";
			}
		}
		@page blank {
			size: a4 portrait;
			/*size:195mm 271mm;*/
			margin: 10mm 10mm;
			padding: 0;
			@top-left {
				content: normal
			}
			@bottom-left {
				content: normal
			}
			@top-right {
				content: normal
			}
			@bottom-right {
				content: normal
			}
			@bottom-center {
				content: normal
			}
		}
		#pagenumber:before {
			content: counter(page);
		}
		#pagecount:before {
			content: counter(pages);
		}
		/* Used for generating Table of content */
		#toc a::after {
			content: leader('.') target-counter(attr(href), page);
		}
		/* Use this class for
        first level titles */
		.page_break_before {
			page-break-before: always;
		}
		/* Use this class for forcing page break inside pdf */
		.page_breaker {
			page-break-after: always;
		}
		.blank {
			page : blank;
		}
		.version {
			page: version;
		}
		@page :[counter(page)=3] {
			margin-top:0;
			margin-right:0;
		@bottom-center {
			content: "第N页";
			font-family:SimSun,'宋体';
			font-size:10px;
		}
		}
		@page version {
			@bottom-left {
				content: "version";
				margin: 0;
				padding: 0;
				font-size: 8pt;
				font-style:italic;
				font-family:SimSun,'宋体';
			}
		}
		/* footnote */
		.notebox {
			page: notebox;
		}
		@page notebox {
			page-break-after: auto;
		}
		@media print {
			.notebox {
				position: relative;
			}
			.notebox .content {
				width: 100%;
				margin-bottom: 0px;
			}
			.notebox .footnote {
				position: absolute;
				top: 265mm;
				left: 0;
				z-index: 100;
				border-top: 1pt solid #000;
				width: 100%;
				font-size: 8.76pt;
			}
		}
		body {
			line-height: 100%;
			font-family:SimSun,'宋体';
			/*width:178mm;*/
			margin: 0;
		}

		.page {
			/*size:195mm 271mm;*/
			height: 100mm;
			/*height:246mm; 16K*/
			border: 0px;
		}

		.font {
			font-size: 10px;
			line-height: 20px;
		}
		.xhx {
			border-bottom:solid 1px #000000;
		}
		td{
			border-top:solid 1px #000000;
			border-bottom:solid 0px #000000;
			border-left:solid 1px #000000;
			border-right:solid 0px #000000;
			display:inline-black;
			padding:6px 0px 6px 0px;
			margin:0px;
		}
		tr td:last-child {
			border-right:solid 1px #000000;
		}
		table { page-break-inside:auto; -fs-table-paginate:paginate;border-spacing: 0;border-bottom:solid 1px #000000;}
	</style>
</head>
<body>

<div style="width:90%;margin:0 auto">
	<div>
		<h2 style="text-align: center;">${name}</h2>
		<div class="Section1">
			<div style="width:100%;overflow:hidden;">
				<div style="float:left;">
					<span>货物交接单号:</span>
                    <img src="${zhwjjdh}" style="height:50px;width:150px;" />
				</div>
				<div style="float:right;">
					<span>生成时间:2021-12-16</span>
				</div>
			</div>

			<table cellspacing="0" cellpadding="0" style="width:100%;margin:0;text-align: center;border-bottom:solid 0px #000000;" rules="all" border="0">
				<thead style="display:table-header-group;border:0px;cellpadding:0;cellspacing:0;height:0px;">

				</thead>
				<tbody>
				<tr style="border-bottom: none">
					<td width="15%">工程名称</td>
					<td width="85%" style="text-align:left;padding-left:10px;"></td>
				</tr>
				<tr>
					<td >合同名称</td>
					<td  style="text-align:left;padding-left:10px;"></td>
				</tr>
				<tr>
					<td >合同编号</td>
					<td style="text-align:left;padding-left:10px;"></td>
				</tr>
				<tr>
					<td >违约事实</td>
					<td style="text-align:left;padding-left:10px;"></td>
				</tr>
				<tr>
					<td >约谈情况</td>
					<td style="text-align:left;padding-left:10px;"></td>
				</tr>
				<tr>
					<td height="50px">供应商</td>
					<td height="80px" style="text-align:left;padding-left:10px;"></td>
				</tr>
				<tr>
					<td height="50px">物资公司</td>
					<td height="80px"></td>
				</tr>
				<tr>
					<td height="50px">项目管理部门</td>
					<td height="80px" style="text-align:left;padding-left:10px;"></td>
				</tr>
				<tr>
					<td height="50px">物资部</td>
					<td height="80px" style="text-align:left;padding-left:10px;"></td>
				</tr>
				<tr>
					<td height="50px">财务部</td>
					<td height="80px" style="text-align:left;padding-left:10px;"></td>
				</tr>
				</tbody>
				<tfoot style="display:table-footer-group;">
				<tr>
					<td style="height: 0px;padding: 0px;border: none;border-bottom: solid 1px #000000;" colspan="2"></td>
				</tr>
				</tfoot>
			</table>
			<p>注:1.涉及技术的违约事实,由项目管理部门确认;涉及商务的违约事实,由物资公司/供应中心确认;技术、商务均涉及的,由项目管理部门,由物资公司/供应中心确认。各个单位可根据实际情况增加其他部门(单位)确认。
			</p>
		</div>
	</div>
</div>
</body>
</html>


调用主函数生成

import com.sgcc.cn.utils.PdfHelper;

import java.io.File;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;

public class test {
    public static void main(String[] args) {
        try {
            PdfHelper pdfHelper = new PdfHelper();
            Map<String,Object> map = new HashMap<>();
            map.put("name","唯有碎银解千愁");
            FileOutputStream out = new FileOutputStream(new File( "D:\\1.pdf"));
            pdfHelper.createPDF(map,out,"gererate.ftl");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

电子化模版嵌套表格

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta charset="UTF-8" />
	<title>调配申请单</title>
	<style type="text/css">
		body{width:100%;}
		@media screen and (max-width: 768px) {
			body{font-size: 14px;}
			table{table-layout: fixed;}  }
		p{line-height: 26px;margin-right:15px;}
		table td p{line-height:6px;margin-right:0;padding-right:0;padding-left:0;}
		span{margin:0;padding:0;}
	</style>
	<style type="text/css" mcs_bogus="1">
		@page {
			size: a4 landscape;/*210*297*/
			/*size:195mm 271mm;*/
			margin: 8mm 8mm;
			padding: 0;
			/*border:solid #F00 1px;*/
			background-image:url(images/water.gif);
			background-repeat:no-repeat;
			background-position:center;
			@top-left {
				content: element(header);
			}
			@bottom-left {
				content: element(footer);
			}
			@bottom-center {
				content: "--- " counter(page) " ---" counter(main-heading);
			}
			@bottom-right {
				content: "";
			}
		}
		@page blank {
			size: a4 portrait;
			/*size:195mm 271mm;*/
			margin: 10mm 10mm;
			padding: 0;
			@top-left {
				content: normal
			}
			@bottom-left {
				content: normal
			}
			@top-right {
				content: normal
			}
			@bottom-right {
				content: normal
			}
			@bottom-center {
				content: normal
			}
		}
		#pagenumber:before {
			content: counter(page);
		}
		#pagecount:before {
			content: counter(pages);
		}
		/* Used for generating Table of content */
		#toc a::after {
			content: leader('.') target-counter(attr(href), page);
		}
		/* Use this class for
        first level titles */
		.page_break_before {
			page-break-before: always;
		}
		/* Use this class for forcing page break inside pdf */
		.page_breaker {
			page-break-after: always;
		}
		.blank {
			page : blank;
		}
		.version {
			page: version;
		}
		@page :[counter(page)=3] {
			margin-top:0;
			margin-right:0;
		@bottom-center {
			content: "第N页";
			font-family:SimSun,'宋体';
			font-size:10px;
		}
		}
		@page version {
			@bottom-left {
				content: "version";
				margin: 0;
				padding: 0;
				font-size: 8pt;
				font-style:italic;
				font-family:SimSun,'宋体';
			}
		}
		/* footnote */
		.notebox {
			page: notebox;
		}
		@page notebox {
			page-break-after: auto;
		}
		@media print {
			.notebox {
				position: relative;
			}
			.notebox .content {
				width: 100%;
				margin-bottom: 0px;
			}
			.notebox .footnote {
				position: absolute;
				top: 265mm;
				left: 0;
				z-index: 100;
				border-top: 1pt solid #000;
				width: 100%;
				font-size: 8.76pt;
			}
		}
		body {
			line-height: 100%;
			font-family:SimSun,'宋体';
			/*width:178mm;*/
			margin: 0;
		}

		.page {
			/*size:195mm 271mm;*/
			height: 100mm;
			/*height:246mm; 16K*/
			border: 0px;
		}

		.font {
			font-size: 10px;
			line-height: 20px;
		}
		.xhx {
			border-bottom:solid 1px #000000;
		}
		td{
			border-top:solid 1px #000000;
			border-bottom:solid 0px #000000;
			border-left:solid 1px #000000;
			border-right:solid 0px #000000;
			display:inline-black;
			padding:6px 0px 6px 0px;
			margin:0px;
		}
		tr td:last-child {
			border-right:solid 1px #000000;
		}
		table { page-break-inside:auto; -fs-table-paginate:paginate;border-spacing: 0;border-bottom:solid 1px #000000;}
	</style>
</head>
<body>

<div style="width:90%;margin:0 auto">
	<div>
		<h2 style="text-align: center;">调配申请单</h2>
		<div class="Section1">
			<div style="width:95%;overflow:hidden;text-align: right;">
				<div>
					编号:${receive.zdpsq}
				</div>
			</div>
			<table cellspacing="0" cellpadding="0" style="width:100%;margin:0;text-align: center;" rules="all" border="0" frame="void">
				<tbody>
				<tr style="border-bottom: none">
					<td width="10%" >申请单位</td>
					<td width="18%" >${receive.zbutxt}</td>
					<td width="18%" >申请日期</td>
					<td width="18%" >${receive.createDate}</td>
					<td width="18%" >调配类型</td>
					<td width="18%" >市内调配/省内跨市调配</td>
				</tr>
				<tr>
					<td rowspan="3" >需求<br/>描述</td>
					<td colspan="2">调配方式</td>
					<td>划转</td>
					<td>拟调配地址</td>
					<td>${receive.zdpdz}</td>
				</tr>
				<tr>
					<td colspan="2">联系人</td>
					<td>${receive.zxqflxr}</td>
					<td>联系电话</td>
					<td>${receive.zxqflxfs}</td>
				</tr>
				<tr>
					<td colspan="5" style="padding:0px 0px 0px 0px;border:solid 0px #000000">
					<table cellspacing="0" cellpadding="0" style="width:100%;margin:0;text-align: center;border-bottom:solid 0px #000000;" rules="all" border="0">
						<thead style="display:table-header-group;">
							<td>序号</td>
							<td>物料编码</td>
							<td>物料名称</td>
							<td>规格型号</td>
							<td>单位</td>
							<td>需求数量</td>
							<td>备注</td>
						</thead>
						<tbody>
							<#list checkList as item>
								<tr>
									<td>${item_index+1}</td>
									<td>${item.matnr}</td>
									<td>${item.maktx}</td>
									<td>${item.zggxh}</td>
									<td>${item.meins}</td>
									<td>${item.menge}</td>
									<td>${item.remark}</td>
								</tr>
							</#list>
						</tbody>
                        <tfoot style="display:table-footer-group;">
                            <tr>
                                <td style="height: 0px;padding: 0px;border: none;border-bottom: solid 0px #000000;" colspan="5"></td>
                            </tr>
                        </tfoot>
					</table>
					</td>
				</tr>
				<tr>
					<td height="170px">市公司分管领导(签字)</td>
					<td></td>
					<td height="170px">市公司物资部/物资供应中心(签字、盖章)</td>
					<td></td>
					<td height="170px">市公司专业部门(签字、盖章)</td>
					<td></td>
				</tr>
				<tr>
					<td colspan="6">本调配单一式四份,省/地市物资部、省物资公司/县公司供应分中心、调入方和调出方各一份</td>
				</tr>
				</tbody>
			</table >
		</div>
	</div>
</div>
</body>
</html>

如果不想把文件存在本地,想在生成之后返回流文件,代码如下

public byte[] createPDF(Map<String, Object> data, String templateName) throws Exception {
        // 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
        Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        // 指定FreeMarker模板文件的位置
        ITextRenderer renderer = new ITextRenderer();
        String simsun = System.getProperty("user.dir") + "/simsun.ttc";
        String arialuni = System.getProperty("user.dir") + "/arialuni.ttf";
        File fondFile = new File(simsun);
        if (!fondFile.exists()) {
            getFondPath();
        }
        // 设置 css中 的字体样式(暂时仅支持宋体和黑体)
        renderer.getFontResolver().addFont(simsun, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
        renderer.getFontResolver().addFont(arialuni, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

        StringTemplateLoader stringLoader = new StringTemplateLoader();
        String templateString = getFileData("templates/" + templateName);
        stringLoader.putTemplate("myTemplate", templateString);
        cfg.setTemplateLoader(stringLoader);
        Template tpl = cfg.getTemplate("myTemplate", "utf-8");
        StringWriter writer = new StringWriter();

        // 把null转换成""
        Map<String, Object> dataMap = CommunalTool.objectToMap(data);
        // 将数据输出到html中
        tpl.process(data, writer);
        writer.flush();

        String html = writer.toString();
        // 把html代码传入渲染器中
        renderer.setDocumentFromString(html);
       	ByteArrayOutputStream out = new ByteArrayOutputStream();
        renderer.layout();
        renderer.createPDF(out);
        renderer.finishPDF();
        byte[] fileByte = out.toByteArray();
        out.flush();
        out.close();
        return fileByte;
    }

在文件中添加图片,代码如下

在ftl文件中添加代码

<span>货物交接单号:</span>
<img src="${zhwjjdh}" style="height:50px;width:150px;" />

用java代码把图片转换成base64
/**
     * 将图片转成base64 字符串
     *
     * @param path 文件路径
     * @return
     * @throws Exception
     */
    public static String encodeBase64Picture(String path) throws Exception {
        File file = new File(path);
        FileInputStream inputFile = new FileInputStream(file);
        byte[] buffer = new byte[(int) file.length()];
        inputFile.read(buffer);
        inputFile.close();
        String result = new BASE64Encoder().encode(buffer);
        if (StringUtils.isNotBlank(result)) {
            result = "data:image/png;base64," + result;
        } else {
            result = null;
        }
        return result;
    }

如果想要动态读取模版也可以

templateString  这个就是模版字符串,然后你可以写一个扩展方法去读取,如你想从数据库中存储和读取
String templateString = getFileData("templates/" + templateName);
stringLoader.putTemplate("myTemplate", templateString);

设置mysql数据库存储文件的字段为大字段类型blob

// 这里只提供一个公共方法,读取文件后返回一个字符串。
public String uploadModelSql(MultipartFile file) {
        BufferedReader reader = null;
        StringBuffer sbf = new StringBuffer();
        try {
            // Reader read = new InputStreamReader(file.getInputStream(), "UTF-8");
            Reader read = new InputStreamReader(file.getInputStream(), "utf-8");
            reader = new BufferedReader(read);
            String tmpString = null;

            //一行一行的读取文件里面的内容,这个是防止文件存储数据库之后,在读取模版布局问题
            while ((tmpString = reader.readLine()) != null) {
                sbf.append(tmpString).append("\r\n");
            }
            if (sbf == null) {
                throw new RuntimeException("文件内容为空!");
            }
            return sbf.toString();

        } catch (IOException e) {
            e.printStackTrace();
            return sbf.toString();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

模版下载

/**
     * 下载模版文件
     *
     * @param response
     * @param id
     * @return
     */
    @RequestMapping("/downLoad")
    public void downLoadFile(HttpServletResponse response, @RequestParam String id) {
      
        try {
        	//根据id查询数据库中的记录
            SignatureTemplatesFile record = signatureTemplatesFileService.downLoadFile(id);
            // 清空response
            response.reset();
            // 设置response的Header,record.getTemplateName() 文件名称
            response.addHeader("Content-Disposition", "attachment;filename=" + new String(record.getTemplateName()));
            // response.addHeader("Content-Length", "" + file.length());
            OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");
            //大字段中的字符串,转换成byte数组
            byte[] bytes = record.getTemplateFile().getBytes();
            toClient.write(bytes);
            toClient.flush();
            toClient.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

总结

代码写的有点乱,总体来说该有的功能都有了,如:从jar中读取字体和ftl模版,如果你不想从jar中读取,可以自己改造一下,就是不管你是从其他地方读文件或者从数据库中字符串,总之你要把他转换成字符串,最后给(templateString)它就行。还有PDF生成的时候也可以返回流文件,你可以把返回的流文件,存储fastDFS(文件管理插件)上面去统一管理。

  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值