【SpringBoot应用篇】SpringBoot+JasperReport导出PDF
JasperReport简介
JasperReport是一个强大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成PDF,HTML,或者XML格式。该库完全由Java写成,可以用于在各种Java应用程序,包括J2EE,Web应用程序中生成动态内容。只需要将JasperReport引入工程中即可完成PDF报表的编译、显示、输出等工作。
在开源的JAVA报表工具中,JasperReport发展是比较好的,比一些商业的报表引擎做得还好,如支持了十字交叉报表、统计报表、图形报表,支持多种报表格式的输出,如PDF、RTF、XML、CSV、XHTML、TEXT、DOCX以及OpenOffice。
数据源支持更多,常用 JDBCSQL查询、XML文件、CSV文件、HQL(Hibernate查询),HBase,JAVA集合等。还允许你义自己的数据源,通过JASPER文件及数据源,JASPER就能生成最终用户想要的文档格式。
JasperReport的开发步骤
生命周期
通常我们提到PDF报表的时候,浮现在脑海中的是最终的PDF文档文件。在JasperReports中,这只是报表生命周期的最后阶段。通过JasperReports生成PDF报表一共要经过三个阶段,我们称之为 JasperReport的生命周期,
这三个阶段为:
设计(Design)阶段、
执行(Execution)阶段
输出(Export)阶段,如下图所示:
设计阶段(Design):所谓的报表设计就是创建一些模板,模板包含了报表的布局与设计,包括执行计算的复杂公式、可选的从数据源获取数据的查询语句、以及其它的一些信息。模板设计完成之后,我们将模板保存为JRXML文件(JR代表JasperReports),其实就是一个XML文件。
执行阶段(Execution): 使用以JRXML文件编译为可执行的二进制文件(即.Jasper文件)结合数据进行执行,填充报表数据
输出阶段(Export):数据填充结束,可以指定输出为多种形式的报表
执行流程
1、JRXML:报表填充模板,本质是一个XML.
JasperReport已经封装了一个dtd,只要按照规定的格式写这个xml文件,那么jasperReport就可以将其解析最终生成报表,但是jasperReport所解析的不是我们常见的.xml文件,而是.jrxml文件,其实跟xml是一样的,只是后缀不一样。
2、Jasper:由JRXML模板编译生成的二进制文件,用于代码填充数据。
解析完成后JasperReport就开始编译.jrxml文件,将其编译成.jasper文件,因为JasperReport只可以对.jasper文件进行填充数据和转换,这步操作就跟我们java中将java文件编译成class文件是一样的
3、Jrprint:当用数据填充完Jasper后生成的文件,用于输出报表。
这一步才是JasperReport的核心所在,它会根据你在xml里面写好的查询语句来查询指定是数据库,也可以控制在后台编写查询语句,参数,数据库。在报表填充完后,会再生成一个.jrprint格式的文件(读取jasper文件进行填充,然后生成一个jrprint文件)
4、Exporter:决定要输出的报表为何种格式,报表输出的管理类。
5、JasperReport可以输出多种格式的报表文件,常见的有Html,PDF,xls等
综上我们得知,对于使用JasperReport进行开发,我们重点关注只有如下四点:
制作报表模板
模板编译
构造数据
填充模板数据
模板工具Jaspersoft Studio
概述
Jaspersoft Studio是JasperReports库和JasperReports服务器的基于Eclipse的报告设计器; 它可以作为Eclipse插件或作为独立的应用程序使用。Jaspersoft Studio允许您创建包含图表,图像,子报表,交叉表等的复杂布局。您可以通过JDBC,TableModels,JavaBeans,XML,Hibernate,大数据(如Hive),CSV,XML / A以及自定义来源等各种来源访问数据,然后将报告发布为PDF,RTF, XML,XLS,CSV,HTML,XHTML,文本,DOCX或OpenOffice。
Jaspersoft Studio 是一个可视化的报表设计工具,使用该软件可以方便地对报表进行可视化的设计,设计结果为格式.jrxml 的XML 文件,并且可以把.jrxml 文件编译成.jasper 格式文件方便 JasperReport 报表引擎解析、显示。
安装配置
到 JasperReport官网下载 https://community.jaspersoft.com/community-download
当然我们可以直接使用下载好的这个软件
双击安装今天资料中的安装包
下一步下一步的安装即可
面板介绍
Report editing area (主编辑区域)中,您直观地通过拖动,定位,对齐和通过 Designer palette(设计器调色板)对报表元素调整大小。JasperSoft Studio 有一个多标签编辑器,Design,Source和Preview:
Design tab:当你打开一个报告文件,它允许您以图形方式创建报表选中
Source tab:包含用于报表的 JRXML 源代码。
Preview tab:允许在选择数据源和输出格式后,运行报表预览。
Repository Explorer view:包含 JasperServer 生成的连接和可用的数据适配器列表
Project Explorer view:包含JasperReports 的工程项目清单
Outline view:在大纲视图中显示了一个树的形式的方式报告的完整结构。
Properties view:通常是任何基于 Eclipse 的产品/插件的基础之一。它通常被填充与实际所选元素的属性的信息。这就是这样,当你从主设计区域(即:一个文本字段)选择一个报表元素或从大纲,视图显示了它的信息。其中一些属性可以是只读的,但大部分都是可编辑的,对其进行修改,通常会通知更改绘制的元素(如:元素的宽度或高度)。
Problems view:显示的问题和错误,例如可以阻断报告的正确的编译。
Report state summary 提供了有关在报表编译/填充/执行统计用户有用的信息。错误会显示在这里
基本使用
模板制作
(1)打开Jaspersoft Studio ,新建一个project, 步骤: File -> New -> Project-> JasperReportsProject
(2) 新建一个Jasper Report模板,在 Stidio的左下方Project Explorer 找到刚才新建的Project (我这里新建的是DemoReport),步骤:项目右键 -> New -> Jasper Report
(3)选择 Blank A4 (A4纸大小的模板),然后 Next 命名为demo.jrxml
如图所示,报表模板被垂直的分层,每一个部分都是一个Band,每一个Band的特点不同:
Title(标题):只在整个报表的第一页的最上端显示。只在第一页显示,其他页面均不显示。
Page Header(页头):在整个报表中每一页都会显示。在第一页中,出现的位置在 Title Band的下面。在除了第一页的其他页面中Page Header 的内容均在页面的最上端显示。
Page Footer(页脚):在整个报表中每一页都会显示。显示在页面的最下端。一般用来显示页码。
Detail 1(详细):报表内容,每一页都会显示。
Column Header(列头):Detail中打印的是一张表的话,这Column Header就是表中列的列头。
Column Footer(列脚):Detail中打印的是一张表的话,这Column Footer就是表中列的列脚。
Summary(统计):表格的合计段,出现在整个报表的最后一页中,在Detail 1 Band后面。主要是用来做报表的合计显示。
编译模板
右键单机模板文件 -> compile Report 对模板进行编译,生成.jasper文件
入门案例
需求:
环境准备
1、导入依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.19.0</version>
</dependency>
</dependencies>
2、准备中文字体资源文件
jasperReports本身对中文的支持不够好,所以如果涉及到中文,需要自己准备中文的资源,现在已“华文宋体为例”
把图中的文件夹和properties文件直接拷贝到项目的resources文件夹下
https://gitee.com/zysheep/springboot-jasperreport/tree/master/src/main/resources/stsong
如下效果:
导出一个最基本的PDF文件
第一步:使用Jaspersoft Studio制作一个简单的模板
1、 创建新模板,删除不需要的Band
2、画边框和线
3、添加几个静态的文本
4、添加变量
在右下角修改变量名称
5、使用变量
6、有中文的设置字体为华文宋体,并居中显示
7、Ctrl+s保存,右键编译模板
把demo.jasper文件放到项目的resorces目录下
第三步:测试代码
/**
* @ClassName: Demo01
* @Description: 输出pdf和html,输出pdf乱码问题
*/
public class Demo01 {
public static void main(String[] args) throws IOException {
// 1.文件的输入流
InputStream resourceAsStream = Demo01.class.getClassLoader().getResourceAsStream("jasper/demo.jasper");
// 设定目标文件输出的路径
String desFilePath = "./src/Field01.pdf";
FileOutputStream outputStream = new FileOutputStream(desFilePath);
try {
// 2.创建JasperPrint,向jasper文件中填充数据
Map parameters = new HashMap<>();
parameters.put("username","封于修");
parameters.put("phone","15096001234");
// 3.将JasperPrint已PDF的形式输出
JasperPrint jasperPrint = JasperFillManager.fillReport(resourceAsStream, parameters, new JREmptyDataSource());
// 通过JasperExportManager管理工具进行报表输出文档,此处设定为输出Html文件.
//JasperExportManager.exportReportToHtmlFile(jasperPrint,desFilePath);
// JasperExportManager.exportReportToPdfFile(jasperPrint,desFilePath);
JasperExportManager.exportReportToPdfStream(jasperPrint, outputStream);
} catch (JRException e) {
e.printStackTrace();
}finally {
outputStream.flush();
}
}
}
效果:
导出用户列表
需求
数据直接从数据库中获取
第一步:制作模板
1、创建新模板,删除不需要的Band,保留title、Column Header、Detail
2、配置数据连接
使用JDBC数据源填充数据:使用Jaspersoft Studio 先要配置一个数据库连接
填写数据源的类型,选择“DatabaseJDBC Connection”
配置数据库信息
这一步,需要: (1)给创建的这个数据连接起个名字; (2)根据数据库选择驱动类型; Jaspersoft Studio 已经内置了很多常用数据库的驱动,使用的时候直接选就可以了。当然,如果这还满足不了你的话,你还可以添加你指定的 JDBC 驱动 jar 包。
3、读取表中属性
左下角右击模板名称选择 Dataset and Query
点击后:
点击“OK”按钮
左下角的Fields中就有了我们想要的属性字段了
4、模板如下
把编译后的jasper文件放入到项目resources中
第三步:完成代码
@Slf4j
@RestController
public class Demo05 {
@Autowired
private HikariDataSource hikariDataSource;
@GetMapping(value = "/downLoadPDFforDB",name = "导出PDF")
public void downLoadPDF(HttpServletResponse response) throws Exception{
// 获取模板文件
String ctxPath = ClassUtils.getDefaultClassLoader().getResource("jasper").getPath();
String filePath = ctxPath+"/demo05.jasper";
ServletOutputStream outputStream = response.getOutputStream();
// 文件名
String filename="系统日志信息.pdf";
// 设置两个头 一个是文件的打开方式 一个是mime类型
response.setHeader( "Content-Disposition", "attachment;filename=" + new String(filename.getBytes(),"ISO8859-1"));
response.setContentType("application/pdf");
// 文件的输入流
InputStream inputStream = new FileInputStream(filePath);
//2.创建JasperPrint,向jasper文件中填充数据
Map parameters = new HashMap<>();
//3.将JasperPrint已PDF的形式输出
JasperPrint jasperPrint = JasperFillManager.fillReport(inputStream,parameters,hikariDataSource.getConnection());
//导出
JasperExportManager.exportReportToPdfStream(jasperPrint,outputStream);
}
}
数据从后台获取
从上面效果来看,导出的日期格式有问题,其实数据库表中的数据很多情况下都需要做一些业务处理才能被使用的,所以再学习一下数据经过处理后再导出。
第一步:创建模板
1、创建新模板,删除不需要的Band
2、创建4个field
这4个field的名称要和User实体类中的属性名称要保持一致,但是注意这里有一个hireDateStr
3、制作如下模板
第二步:把编译的后的模板文件放入到项目resources中
第三步:修改UserController中的代码
@GetMapping(value = "/downLoadPDF",name = "导出PDF")
public void downLoadPDF(HttpServletResponse response) throws Exception{
//userService.downLoadPDFByDB(response);
userService.downLoadPDF(response);
}
第四步:完成UserService代码
1、在User实体类
@Data
public class User {
private String id;
private String userName;
private String telphone;
private String hireDateStr;
}
2、Ctroller层代码实现PDF导出
@RestController
public class Demo03 {
@GetMapping(value = "/downLoadPDF",name = "导出PDF")
public void downLoadPDF(HttpServletResponse response) throws Exception{
// 获取模板文件
String ctxPath = ClassUtils.getDefaultClassLoader().getResource("jasper").getPath();
String filePath = ctxPath+"/demo03.jasper";
ServletOutputStream outputStream = response.getOutputStream();
// 文件名
String filename="用户列表数据.pdf";
// 设置两个头 一个是文件的打开方式 一个是mime类型
response.setHeader( "Content-Disposition", "attachment;filename=" + new String(filename.getBytes(),"ISO8859-1"));
response.setContentType("application/pdf");
// 文件的输入流
InputStream inputStream = new FileInputStream(filePath);
//2.创建JasperPrint,向jasper文件中填充数据
Map parameters = new HashMap<>();
// 查询所有数据
List<User> userList = selectAll();
// 给hireDateStr赋值
JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(userList);
//3.将JasperPrint已PDF的形式输出
JasperPrint jasperPrint = JasperFillManager.fillReport(inputStream,parameters,dataSource);
//导出
JasperExportManager.exportReportToPdfStream(jasperPrint,outputStream);
}
// 模拟读取数据库,查询所有数据
List<User> selectAll() {
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User user = new User();
user.setId(UUID.randomUUID().toString().replace("-",""));
user.setUserName("封于修"+i);
user.setTelphone("123456789");
user.setHireDateStr(new SimpleDateFormat("yyyy-MM-dd mm:hh:ss").format(new Date()));
users.add(user);
}
return users;
}
}
PDF导出图片
第一步:创建模板
1、创建新模板,删除不需要的Band
2、创建Fields,Clss类型都为java.lang.String,image设置为java.io.InputStream
3、设置模板基本信息
4、添加图片数据
a)、在JasperReport 拖拽image 到detail中
弹出 create new image element选择Image creation mode 中的最后一个No image
b)、选中刚才添加的image . 在右侧Properties -> Image中,Expression 设置刚才定义的image
On Errror Type: 设置为icon。如果图片不存在不保错,显示icon图标
最终的模板如下:
第二步:把编译后的模板放入到项目中
第三步:代码实现
1、UserImage 实体类
@Data
public class UserImage {
private String id;
private String userName;
private String telphone;
private String hireDateStr;
private InputStream image;
}
2、Ctroller层代码实现PDF导出
@RestController
public class Demo04 {
@GetMapping(value = "/downLoadPDFImage",name = "导出PDF")
public void downLoadPDF(HttpServletResponse response) throws Exception{
// 获取模板文件
String ctxPath = ClassUtils.getDefaultClassLoader().getResource("jasper").getPath();
String filePath = ctxPath+"/demo04.jasper";
ServletOutputStream outputStream = response.getOutputStream();
// 文件名
String filename="用户列表数据.pdf";
//设置两个头 一个是文件的打开方式 一个是mime类型
response.setHeader( "Content-Disposition", "attachment;filename=" + new String(filename.getBytes(),"ISO8859-1"));
response.setContentType("application/pdf");
// 文件的输入流
InputStream inputStream = new FileInputStream(filePath);
//2.创建JasperPrint,向jasper文件中填充数据
Map parameters = new HashMap<>();
// 查询所有数据
List<UserImage> userList = selectAll();
// 给hireDateStr赋值
JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(userList);
//3.将JasperPrint已PDF的形式输出
JasperPrint jasperPrint = JasperFillManager.fillReport(inputStream,parameters,dataSource);
String desFilePath = "./src/demo04.html";
// 导出PDF
JasperExportManager.exportReportToPdfStream(jasperPrint,outputStream);
// 导出为html
//JasperExportManager.exportReportToHtmlFile(jasperPrint,desFilePath);
}
// 模拟读取数据库,查询所有数据
static List<UserImage> selectAll() {
ArrayList<UserImage> userImages = new ArrayList<>();
for (int i = 0; i < 5; i++) {
UserImage userImage = new UserImage();
userImage.setId(UUID.randomUUID().toString().replace("-",""));
userImage.setUserName("封于修"+i);
userImage.setTelphone("123456789");
userImage.setHireDateStr(new SimpleDateFormat("yyyy-MM-dd mm:hh:ss").format(new Date()));
InputStream resourceAsStream = Demo04.class.getClassLoader().getResourceAsStream("static/1.png");
userImage.setImage(resourceAsStream);
userImages.add(userImage);
}
return userImages;
}
}
导出的效果: