java(itextpdf+FreeMarker)生成PDF文件(带logo)从前端post提交数据到后端

前端页面

在这里插入图片描述

效果图

在这里插入图片描述

实现思路

  1. 需要前端录入一部分数据
  2. 框架使用:itext+FreeMarker ,layui前端框架+ssm框架
  3. 前端POST提交数据后以 json字符串存入数据库表A中CLOB类型的content字段中
  4. POST提交后回调函数按表A中对应数据ID查找数据生成PDF并直接下载

POM.XML引入依赖

		<!-- freemarker engine -->
		<dependency>
			<groupId>org.freemarker</groupId>
			<artifactId>freemarker</artifactId>
			<version>${freemarker.version}</version>
		</dependency>
			<dependency>
			<groupId>com.lowagie</groupId>
			<artifactId>itext</artifactId>
			<version>2.1.7</version>
			<exclusions>
				<exclusion>
					<artifactId>bcmail-jdk14</artifactId>
					<groupId>bouncycastle</groupId>
				</exclusion>
				<exclusion>
					<artifactId>bcprov-jdk14</artifactId>
					<groupId>bouncycastle</groupId>
				</exclusion>
				<exclusion>
					<artifactId>bctsp-jdk14</artifactId>
					<groupId>bouncycastle</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.xhtmlrenderer</groupId>
			<artifactId>flying-saucer-pdf</artifactId>
			<version>9.0.3</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>itextpdf</artifactId>
			<version>5.4.5</version>
		</dependency>
			<dependency>
			<groupId>com.itextpdf.tool</groupId>
			<artifactId>xmlworker</artifactId>
			<version>5.4.5</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>itext-asian</artifactId>
			<version>5.2.0</version>
		</dependency>

前端JS代码

说明:前端对象为Person,可以添加删除行的那部分数据使用ArrayList对象为PrintData,Person中对应了一条账单Bill的ID

	function submitPrint(){
		//非空校验
		if(!$("#inputForm").valid()){return false;}
		//获取表单数据(对象)
		var data =  $("#inputForm").serialize();
		//POST异步请求提交数据储存到数据库
		axios({
		    method: 'post',
		    url: "${ctx}/xxx/xxxXXXxxx/submitPrintData", //此方法下方列出
		    data: $("#inputForm").serialize()
		}).then(function (data) {
			//浏览器返回的响应码
			var status = data.status;
			//response数据中的msg值对应的数据(是表A中此条需要被打印数据的id)
			var contentId = data.data.msg;
		    if(status =='200'){//正常
		    	layer.alert("保存成功,下载中...",{// 保存成功 
					closeBtn:0,
		    		btn:["确定"],
		    		yes:function(index){
		    			var contentId = data.data.msg;
		    			layer.close(index);
		    			//生成PDF文件(根据id找到数据,输出)
		    			window.location.href="${ctx}/xxx/xxxXXXxxx/print?contentId="+contentId; //此方法下方列出
		    		}
		    	})
		    }else{
		    	layer.alert("失败",
						{title:'提示',btn:'确认'});//保存失败
		    }
		}).catch(function (error) {
			layer.alert("失败",
						{title:'提示',btn:'确认'});//保存失败
		});
	} 

后端储存数据代码

@ResponseBody
@RequestMapping(value = "submitPrintData",method=RequestMethod.POST)
public Map<String,String> submitPrintData(Person person,
		Model model,HttpServletRequest request, HttpServletResponse response ){
	//返回前端的消息集合创建
	HashMap<String,String> mapForResult = new HashMap<>();
	//什么什么条件是存储数据失败的,返回给前端
	if(....) {
		mapForResult.put("status", "400");
		return  mapForResult;
	}
	//删除以前A表中打印的数据(为了留存痕迹,只是标记删除,没有物理删除)
	AService.deleteByBillId(billId);
	//创建A表新对象
	A a = new A();
	//创建需要存进A表中的数据的容器,使用map,
	Map<String, Object> map=new HashMap<>();
	//把map装满数据
	map.put("date", ...);
	map.put("noStr", ...);
	...
	//把装满数据map变成json串存进数据库表A
	JSONObject joForContent = new JSONObject(map);
	a.setRepContents(joForContent.toJSONString());
	aService.save(a);
	String contentId = a.getId();
	mapForResult.put("msg", contentId);//前端通过此id获取数据后取出数据进行打印
	mapForResult.put("status", "200");
	return  mapForResult;
}

后端print代码

@RequestMapping(value = "print")
public void print(Model model,HttpServletRequest request, HttpServletResponse response )
				throws Exception {
	//获取想打印数据id
	String contentId = request.getParameter("contentId");
	//查询数据
	A a = aService.get(contentId);
	//查出数据转为map
	Map<String,Object> map = (Map<String,Object>)JSONObject.parseObject(a.getRepContents());
	/** 开始生成PDF方法*/
	//设置编码
	response.setCharacterEncoding("UTF-8");
	//设置生成的PDF文件名
	response.setHeader("Content-Disposition","attachment;filename=" +
			URLEncoder.encode("武林协会收费清单.pdf","UTF-8"));
	//freemarker去指定路径寻找画好的模板,带着数据去模板上,(此处模板枚举对应的)
	String html=freemarkerService.getHtml(
			PdfEnum.武林协会收费清单.getName(),map);  //此方法在下方
	//工具类,把带有数据的模板传到前端生成文件
	PdfUtils.createPdf(response, html); //此方法在下方
}

freemarkerService代码

@Component //(把普通pojo实例化到spring容器中,相当于配置文件中的<bean id="" class=""/>)
public class FreemarkerService {
	@Autowired
	private Configuration configuration;  //package freemarker.template;freemarker.template包里的类
	
	public String getHtml(String fileName,Map<String,Object> map) throws Exception {
		//指定模板所在位置
		String freemarkerPath="templates\\xxxxxx\\freemarkexxxxxxxxx\\";
		//springd加载资源文件的ClassPathResource方法
		Resource resource = new ClassPathResource(freemarkerPath);
		//java.io.File:getAbsoluteFile是不会抛出异常的,返回:绝对路径名字符串,此处为后面模板中引入图片做准备
		map.put("basePath", resource.getFile().getAbsoluteFile());
		//按名字获取到模板
		Template template = configuration.getTemplate(fileName);
		//执行 写
		StringWriter writer = new StringWriter();
		template.process(map,writer);
	    writer.flush();
	    writer.close();
	    String html=writer.toString();
	    writer.close();
	    //写成一个“页面”
	    return html;	
	}
}

PdfUtils工具类

public class PdfUtils {
	public static void createPdfForBsDebitNote(HttpServletResponse response,String html) throws Exception{
		//创建文件
    	Document doc = new Document(PageSize.A4,0,0,0,0);
    	//输出流:资源到前端一个文件的过程
	    OutputStream stream = response.getOutputStream();
	    //package com.itextpdf.text.pdf; 添加到此文档的内容将被写入outputstream.
	    PdfWriter pdfWriter = PdfWriter.getInstance(doc,stream); 
	    pdfWriter.setPageEvent(new PdfPageEventHelper(){
	            @Override
	            public void onEndPage(PdfWriter writer, Document document) {
	                PdfContentByte cb = writer.getDirectContent();
	                try{
	                    cb.setFontAndSize(BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED),6);
	                } catch (com.itextpdf.text.DocumentException e) {
	                    e.printStackTrace();
	                } catch (IOException e) {
	                    e.printStackTrace();
	                }
	                cb.saveState();
	                cb.beginText();
	                cb.endText();
	                cb.restoreState();
	            }
	   });
	   doc.setMargins(10, 10, 10, 10);
	   doc.open();
	   //以下可以使用itextpdf带有的丰富的方法去写,去画,但我们使用并做好了模板,因此把模板搞成字节输入流取代那些方法
	   ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(html.getBytes());
	   //这种将XHTML/CSS或XML流解析为PDF的是被itextpdf接收的并且itextpdf提供了帮助类XMLWorkerHelper,使用后直接搞定
	   XMLWorkerHelper.getInstance().parseXHtml(pdfWriter,doc,byteArrayInputStream);
	   doc.close();
	   stream.close();
    }

}
	

模板(使用的ftl类型文件:网页模板和数据模型的结合体)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
	<title>武林协会收费清单模板</title>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

	<style type="text/css">
		body {
			font-family: Microsoft YaHei;
		}
		@page {
			size: A4;
		}
	</style>
</head>
<!-- 白底黑字 -->
<body bgcolor="#FFFFFF" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
	<!-- 最上方的留白 -->
	<table style="width: 100%; border-collapse: collapse;">
		<tr>
			<td style="width: 100%;height:22px;"><strong>  </strong></td>
		</tr>	
	</table>
	<!-- LOGO和标题 -->
	<table style="width: 100%; border-collapse: collapse;">
		<tr>
			<!-- 第一列为LOGO -->
			<td style="width:25%;height:155px">
				<table>
					<tr>
						<!-- freemarkerPath那写好的路径 LOGO文件路径,使用的gif类型的文件 -->
						<td><img src="${basePath}/logo.gif"  width="181"  height="155"/></td>
					</tr>
				</table>
			</td>
			<!-- 第二列为题目,题目又分很多行 -->
			<td style="width:75%;height:155px">	
				<table>
					<tr><td style="height:18%"> </td></tr>
					<tr><td style="height:18%;font-size:15px;text-align:center;"><strong>武林协会</strong></td></tr>
					<tr><td style="height:16%;font-size:13px;text-align:center;"><strong>北京市丰台区。。。</strong></td></tr>
					<tr><td style="height:18%"></td></tr>
				</table>
			</td>
		</tr>  
	</table>
	<!-- 文字 -->
	<table style="width: 100%; border-collapse: collapse;">
		<tr>
			<td style="width: 100%;height:35px;font-size:20px;text-align:center;"><strong>收款通知单</strong></td>
		</tr>
	</table>
	<!-- 文字(这个地方写的不是很好)以下内容在一个大大大table里 -->
	<table style="width: 100%; border-collapse: collapse; font-size: 12px;">
		<tr>
			<td style="width: 100%; height: 150px;">
				<table>
					<tr>
						<td style="height: 150px;" >
							<table>
								<tr>
									<td style="width: 25%;padding-left: 60px; float: right;font-size: 12px;">${who?if_exists}</td>
									<td style="width: 20%"></td>
									<td style="width: 25%;padding-left: 60px; float:right;font-size: 12px;">NO。。</td>
								</tr>
								<tr>
									<td style="height: 30%"></td>
								</tr>
								<tr>
									<td style="width: 25%;padding-left: 60px; float: right;font-size: 12px;">${..?if_exists}</td>
									<td style="width: 20%"></td>
									<td style="width: 25%;padding-left: 60px; float:right;font-size: 12px;">Date..</td>
								</tr>
							</table>
						</td>
					</tr>
				</table>
			</td>
		</tr>
		<!-- 那条横线 -->
		<tr>
			<td><img src="${basePath}/topLine.gif" width="754" height="2px" /></td>
		</tr>
		<!-- 页面上录的那些文字  -->
		<tr>
			<td style="width: 580px; height: 400px;padding-left: 20px">
				<table style="font-size: 12px;padding-left: 20px">
					<tr>
						<td style="width: 12%;padding-left: 60px; float: left">${zeroName?if_exists}</td>
						<td style="width: 55%;float: left">${zeroContent?if_exists}</td>
						<td style="width: 35%;"></td>
					</tr>
					<tr>
						<td style="width: 12%;padding-left: 60px; float: left">${oneName?if_exists}</td>
						<td style="width: 55%;float: left">${oneContent?if_exists}</td>
						<td style="width: 35%;"></td>
					</tr>
					...
					<tr>
						<td style="height: 20%"></td>
					</tr>
				</table>
				<table style="font-size: 12px;padding-left: 20px">
					<tr><td style="padding-left: 60px; float: left">DUR AND PAYABLE</td></tr>
					<tr>
						<td style="padding-left: 60px; float: left">--------------------------------------------------------</td>
					</tr>
					<tr><td style="padding-left: 60px; float: left">Date :${Date?if_exists}</td></tr>
				</table>
			</td>
			<!-- 右侧那条竖线  -->
			<td><img src="${basePath}/rightLine.gif" width="2" height="435px"  /></td>
			<!-- 右侧那条数据  -->
			<td style="width: 290px; height: 435px;" colspan='1'>
				<table>
					<tr>
						<td colspan="3" style="padding-left: 30px;font-size: 12px;">${currency?if_exists} ${amount?if_exists}</td>
					</tr>
				</table>
			</td>
		</tr>
		<!-- 下方那条横线  -->
		<tr>
			<td><img src="${basePath}/Line.gif" width="754px" height="2px" /></td>
		</tr>
		<!-- 横线下方的字  -->
		<tr>
			<td	style="width:600px;">
				<table style="font-size: 11px;padding-left: 20px">
					<tr  style="padding-left: 60px; float: left; ">
						<td style="padding-left: 60px; float: left;font-size: 10px; ">xxxxxxxxxx</td>
					</tr>
				</table>
			</td>
		</tr>
<!-- 大大大表格结束  -->
	</table>

</body>
</html>

发布了11 篇原创文章 · 获赞 0 · 访问量 221

猜你喜欢

转载自blog.csdn.net/weixin_43885975/article/details/105563626