java整合数据成pdf供下载。
gitee地址:https://gitee.com/Pre_Nie/create-pdf.git
点我直达
先上pom.xml
<?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.5.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>uploadPDF</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>uploadPDF-1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<poi-version>3.11</poi-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</artifactId>
<exclusions>
<exclusion><!-- 去除默认配置 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency> <!-- 支持识别yml配置 -->
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 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>
<!-- 多线程要用到 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<!--thymeleaf依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi-version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi-version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>${poi-version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jfree/jcommon -->
<dependency>
<groupId>jfree</groupId>
<artifactId>jcommon</artifactId>
<version>1.0.16</version>
</dependency>
<!-- https://mvnrepository.com/artifact/jfree/jfreechart -->
<dependency>
<groupId>jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.0.13</version>
</dependency>
</dependencies>
<build>
<finalName>likesTest</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
总体说明一下,一个controller,一个封装数据主体,一个生成pdf主体(util),一个模板(ftl)。大家都很急线上代码。
package com.example.demo.comtroller;
import java.io.UnsupportedEncodingException;
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.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.entity.PdfDataTest;
import com.example.demo.tools.PdfTemplateUtil;
@RestController
@RequestMapping("/pdf")
public class PDFController {
@Autowired
private PdfTemplateUtil pdfUtils;
private Map<String, Object> getStringObjectMap() {
// 模板中的数据,实际运用从数据库中查询
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, "测试", "测试", "测试", "测试adadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsaadadasdsadadsadsadasdsadsadsadasdsadsadsadasdsa"));
detailList.add(new PdfDataTest(111111, "测试", "测试", "测试", "测试"));
detailList.add(new PdfDataTest(222222, "测试", "测试", "测试", "测试"));
data.put("detailList", detailList);
return data;
}
@RequestMapping("/export")
public void exportPdf(HttpServletResponse response){
Map<String, Object> data = getStringObjectMap();
//以上进行数据封装,实际操作自己封装数据
String fileName;
try {
fileName = URLEncoder.encode("xxx报告.pdf", "UTF-8");
pdfUtils.createPDF(data, "createpdf.ftl", fileName, response);
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
实体类
package com.example.demo.entity;
import java.util.List;
public class PdfDataTest {
private Integer column1;
private String column2;
private String column3;
private String column4;
private String column5;
private String column6;
private List<String> one;
private List<String> two;
public List<String> getOne() {
return one;
}
public void setOne(List<String> one) {
this.one = one;
}
public List<String> getTwo() {
return two;
}
public void setTwo(List<String> two) {
this.two = two;
}
public List<String> getThree() {
return three;
}
public void setThree(List<String> three) {
this.three = three;
}
private List<String> three;
public PdfDataTest(Integer column1, String column2, String column3, String column4, String column5,String column6) {
this.column1 = column1;
this.column2 = column2;
this.column3 = column3;
this.column4 = column4;
this.column5 = column5;
this.column6 = column6;
}
public PdfDataTest(Integer column1, String column2, String column3, String column4, String column5) {
this.column1 = column1;
this.column2 = column2;
this.column3 = column3;
this.column4 = column4;
this.column5 = column5;
}
public PdfDataTest(List<String> one,List<String> two,List<String> three) {
this.one = one;
this.two = two;
this.three = three;
}
public PdfDataTest() {
}
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;
}
public String getColumn6() {
return column6;
}
public void setColumn6(String column6) {
this.column6 = column6;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("PdfDataTest{");
sb.append("column1=").append(column1);
sb.append(", column2='").append(column2).append('\'');
sb.append(", column3='").append(column3).append('\'');
sb.append(", column4='").append(column4).append('\'');
sb.append(", column5='").append(column5).append('\'');
sb.append('}');
return sb.toString();
}
}
生成pdf主体类
package com.example.demo.tools;
/**
* @Auther: pre_nie
* @Date: 2019/8/10 12:59
* @Description:
*/
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.util.Locale;
import java.util.Map;
import freemarker.template.Template;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.xhtmlrenderer.pdf.ITextFontResolver;
import org.xhtmlrenderer.pdf.ITextRenderer;
import com.lowagie.text.pdf.BaseFont;
import freemarker.template.Configuration;
import javax.servlet.http.HttpServletResponse;
@Component
public class PdfTemplateUtil {
@Value("${win.fonts.song}")
private String songFonts;
@Value("${win.fonts.hei}")
private String heiFonts;
@Value("${win.fonts.kai}")
private String kaiFonts;
/**
* @param data 模板数据
* @param templateFileName freemarker模板文件名
* @auther : $Mr. Liu$
* @date : 2019/8/9 14:45
* @description : 通过模板导出pdf文件(改进后无返回值)
**/
public void createPDF(Map<String, Object> data, String templateFileName, String fileName, HttpServletResponse response) throws Exception {
// 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
// 指定FreeMarker模板文件的位置
configuration.setClassForTemplateLoading(PdfTemplateUtil.class, "/templates");
ITextRenderer renderer = new ITextRenderer();
ByteArrayOutputStream out = new ByteArrayOutputStream();
StringWriter writer = new StringWriter();
try {
String fonts = System.getProperty("user.dir") + "/fonts/";
//特别注意一下,linux路径只能使用/ win \ / 都可以
File file = new File(fonts);
if ( !file.exists()) {
//TODO 这里可以再次进行字体文件上传的校验,当发现没有上传成功后的补救
}
String path1 = fonts + songFonts;
String path2 = fonts + heiFonts;
String path3 = fonts + kaiFonts;
// 设置 css中 的字体样式(暂时仅支持宋体和黑体) 必须,不然中文不显示
ITextFontResolver fontResolver = renderer.getFontResolver();
System.out.println(path1);
fontResolver.addFont(path1, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
fontResolver.addFont(path2, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
fontResolver.addFont(path3, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
// 设置模板的编码格式
configuration.setEncoding(Locale.CHINA, "UTF-8");
// 获取模板文件
Template template = configuration.getTemplate(templateFileName, "UTF-8");
// 将数据输出到html中
template.process(data, writer);
writer.flush();
String html = writer.toString();
// 把html代码传入渲染器中
renderer.setDocumentFromString(html);
// 设置模板中的图片路径 (这里的images在resources目录下) 模板中img标签src路径需要相对路径加图片名 如<img src="images/xh.jpg"/>
// String url = PdfTemplateUtil.class.getClassLoader().getResource("static/images").toURI().toString();
// renderer.getSharedContext().setBaseURL(url);
renderer.layout();
renderer.createPDF(out, false);
out.flush();
renderer.finishPDF();
response.setContentType("application/x-msdownload");
// 告诉浏览器,当前响应数据要求用户干预保存到文件中,以及文件名是什么 如果文件名有中文,必须URL编码
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
out.writeTo(response.getOutputStream());
} catch (Exception e) {
throw new Exception("生成pdf错误");
} finally {
if (writer != null) {
writer.close();
}
if (out != null) {
out.close();
}
}
}
}
接下来是模板(创建ftl后缀文件放在resources下面,createpdf.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>
<#list detailList as ad>
<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>
<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>
</table>
</#list>
</section>
<!-- 明细 end -->
</body>
</html>
配置文件application.properties
server.port=8080
fonts.hei=SIMHEI.TTF
fonts.kai=SIMKAI.TTF
fonts.song=SIMSUN.TTC
#这里可以切换地址,设置为win的方便本地测试
win.fonts.hei=SIMHEI.TTF
win.fonts.kai=SIMKAI.TTF
win.fonts.song=SI
关于字体在正式环境的问题,有些环境不允许手动配置,这边做了项目读取文件自动上传文件到指定目录,具体做法需要将字体文件放到resource/templates文件夹下
接下来需要读取并上传
代码在config里
package com.example.demo.config;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PDFConfig {
@Bean
public void createFonts(){
String filePath0 = System.getProperty("user.dir")+ File.separator+"fonts";
File file = new File(filePath0);
if (!file.exists()) {
file.mkdirs();
String[] srts = {"templates/SIMSUN.TTC","templates/SIMHEI.TTF","templates/SIMKAI.TTF"};
String[] srt1 = {"/templates/SIMSUN.TTC","/templates/SIMHEI.TTF","/templates/SIMKAI.TTF"};
String[] names = {"SIMSUN.TTC","SIMHEI.TTF","SIMKAI.TTF"};
for(int i =0;i<srts.length;i++) {
InputStream in = this.getClass().getClassLoader().getResourceAsStream(srts[i]);
String filePath = filePath0+ File.separator+names[i];
FileOutputStream fos;
try {
fos = new FileOutputStream(filePath);
byte[] b = new byte[1024];
int len;
while ( (len = (in.read(b))) != -1) {
fos.write(b,0,len);// 写入数据;
}
in.close();
fos.close();// 保存数据
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
,关于模板我也有一点想说的,模板不能够采用js实现动态效果,如果要实现动态效果怎么办,例如一个最侧栏右边有多个行数据,如何进行排版呢。我目前没找到好的方法,我通过修改要渲染的HTML字符串,进行java查找替换进行动态实现。具体做法是在PdfTemplateUtil里
这里将html进行字符替换实现js的效果。好了以上就是了,如果还有问题可以联系我扣扣328380130。