Spring Boot基于iReport使用自定义数据导出复杂PDF

之前我所接触到的iReport制作报表的方式是,将查询数据的SQL卸载报表jrxml文件里,在Java项目里将数据库连接,以及报表需要的一些参数一起传给报表,再由jasperreports将报表导出为所需要的格式。这种方式一般用来导出一些格式相对单一的报表,如表格、单个或多个图形等。

这次遇到了一种比较复杂等情况,一个报表包含两部分:一部分为类似个人信息的固定信息;另一部分是一个表格,包含多条记录,需要分页。如果直接写SQL需要多个数据源,而且涉及到一些条件判断,比较复杂。因此这里采用了在Java代码中将数据组织起来,iReport仅将数据展示出来,不再包含SQL。

首先,处理iReport报表需要添加jasperreports依赖,导出pdf需要依赖itext库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.6.0</version>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asiancmaps</artifactId>
<version>5.1.1</version>
</dependency>

接着将数据查出来,组成需要的结构,然后编译报表(这里每次导出报表都进行编译是为了在更新了报表以后不用重启应用就可以生效),填充数据,使用JasperExportManager工具将报表输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package win.wellcoding.report.controller;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Map;

/**
* @author 旺旺小学酥
* @Time 2018/10/10
*/
@Controller
@RequestMapping("/report")
public class ReportController {

@RequestMapping
public void export(final HttpServletResponse response) throws JRException, IOException {

final Map<String, Object> result = Maps.newHashMap();
// 固定的个人信息
result.put("name", "张三");
result.put("college", "计算机学员");
result.put("serial", "00000001");
// 多条成绩信息
// Lists是Google的guava库中的
// 这里用到了JRDataSource中的JRBeanCollectionDataSource,表示集合数据源
final JRDataSource datasource = new JRBeanCollectionDataSource(
Lists.newArrayList(new Score("语文", 85), new Score("英语", 80), new Score("数学", 90)));
//编译jrxml
final JasperReport jasperReport = JasperCompileManager.compileReport("/Users/xiaoxuesu/record.jrxml");
//渲染加载数据
final JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, result, datasource);

// 添加Header信息,告诉浏览器这是一个附件,以及附件名称
response.setHeader("Content-Disposition", "attachment; 导出文件名称.pdf");
final OutputStream outputStream = response.getOutputStream();
//输出PDF
JasperExportManager.exportReportToPdfStream(jasperPrint, outputStream);
}

public class Score {

private final String subject;
private final Integer score;

public Score(final String subject, final Integer score) {
this.subject = subject;
this.score = score;
}
}
}

报表需要添加4个Parameters:namecollegeserialrecords,其中records参数要注意Parameter Class属性要选JREmptyDataSource,与Score类对应的添加两个Fields。固定的个人信息放在Page Header,不会进行迭代填充,下面多条记录的成绩信息的表头放在Column Header,然后表格内容放在Detail中,会根据数据迭代显示,这里需要注意的是Detail的高度为一条记录的高度,页脚的页码放在Page Footer中,这样一页报表的内容就设计好了,当一页放不下的时候就会自动分页。

iReport报表设计效果

报表XML如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="record" language="groovy" pageWidth="595" pageHeight="842" columnWidth="595" leftMargin="0" rightMargin="0" topMargin="0" bottomMargin="0" uuid="d7e8e1f8-0e73-4388-a5a2-2fd47f9c3a40">
<property name="ireport.zoom" value="0.75"/>
<property name="ireport.x" value="0"/>
<property name="ireport.y" value="0"/>
<style name="table">
<box>
<pen lineWidth="1.0" lineColor="#FFFFFF"/>
</box>
</style>
<style name="table_TH" mode="Opaque" backcolor="#FFFFFF">
<box>
<topPen lineWidth="0.5" lineColor="#FFFFFF"/>
<bottomPen lineWidth="0.5" lineColor="#FFFFFF"/>
</box>
</style>
<style name="table_CH" mode="Opaque" backcolor="#FFBFBF">
<box>
<topPen lineWidth="0.5" lineColor="#FFFFFF"/>
<bottomPen lineWidth="0.5" lineColor="#FFFFFF"/>
</box>
</style>
<style name="table_TD" mode="Opaque" backcolor="#FFFFFF">
<box>
<topPen lineWidth="0.5" lineColor="#FFFFFF"/>
<bottomPen lineWidth="0.5" lineColor="#FFFFFF"/>
</box>
</style>
<parameter name="name" class="java.lang.String"/>
<parameter name="college" class="java.lang.String"/>
<parameter name="serial" class="java.lang.String"/>
<parameter name="records" class="net.sf.jasperreports.engine.JREmptyDataSource">
<parameterDescription><![CDATA[]]></parameterDescription>
</parameter>
<field name="subject" class="java.lang.String"/>
<field name="score" class="java.lang.Integer"/>
<pageHeader>
<band height="350">
<staticText>
<reportElement x="200" y="240" width="58" height="20" uuid="432e96b4-1ad4-4374-8816-3f6ac52c5797"/>
<textElement>
<font fontName="STXinwei" size="18" pdfFontName="STXinwei" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<text><![CDATA[姓名:]]></text>
</staticText>
<staticText>
<reportElement x="200" y="270" width="58" height="20" uuid="c7c2fd8f-2c43-46d1-ade0-54562d7d2f60"/>
<textElement>
<font fontName="STXinwei" size="18" pdfFontName="STXinwei" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<text><![CDATA[院系:]]></text>
</staticText>
<staticText>
<reportElement x="200" y="300" width="58" height="20" uuid="cf77ef1d-26ad-48a1-884f-3ae86a55c899"/>
<textElement>
<font fontName="STXinwei" size="18" pdfFontName="STXinwei" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<text><![CDATA[学号:]]></text>
</staticText>
<textField>
<reportElement x="258" y="240" width="100" height="20" uuid="7d5abdcb-394a-46f0-a853-b5e4226ead9e"/>
<textElement>
<font fontName="STXinwei" size="18" pdfFontName="STXinwei" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<textFieldExpression><![CDATA[$P{name}]]></textFieldExpression>
</textField>
<textField>
<reportElement x="258" y="270" width="100" height="20" uuid="59bde699-9962-40bb-a0b7-e752ab672828"/>
<textElement>
<font fontName="STXinwei" size="18" pdfFontName="STXinwei" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<textFieldExpression><![CDATA[$P{college}]]></textFieldExpression>
</textField>
<textField>
<reportElement x="258" y="300" width="100" height="20" uuid="a1ca2e23-3c26-490c-b5f1-3ad3957ac9fa"/>
<textElement>
<font fontName="STXinwei" size="18" pdfFontName="STXinwei" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<textFieldExpression><![CDATA[$P{serial}]]></textFieldExpression>
</textField>
</band>
</pageHeader>
<columnHeader>
<band height="35">
<staticText>
<reportElement x="157" y="0" width="74" height="35" uuid="c7a2efba-be58-459e-94da-933c56c140ff"/>
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="STXinwei" size="18" pdfFontName="STXinwei" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<text><![CDATA[课程名称]]></text>
</staticText>
<staticText>
<reportElement x="475" y="0" width="38" height="35" uuid="8ec5b983-8d76-4e2a-9d65-03f6108ed7ad"/>
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="STXinwei" size="18" pdfFontName="STXinwei" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<text><![CDATA[成绩]]></text>
</staticText>
</band>
</columnHeader>
<detail>
<band height="40">
<printWhenExpression><![CDATA[$F{name}.length()>15]]></printWhenExpression>
<textField isStretchWithOverflow="true">
<reportElement x="72" y="0" width="275" height="40" isPrintWhenDetailOverflows="true" uuid="ec6b1757-d4e7-447b-b6ee-511a7453c722"/>
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="STXinwei" size="18" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<textFieldExpression><![CDATA[$F{subject}]]></textFieldExpression>
</textField>
<textField isStretchWithOverflow="true">
<reportElement x="450" y="0" width="90" height="40" uuid="b61827c5-a4df-4304-88d6-73bf1deceb2e"/>
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="STXinwei" size="18" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<textFieldExpression><![CDATA[$F{score}]]></textFieldExpression>
</textField>
</band>
</detail>
<pageFooter>
<band height="240">
<textField isBlankWhenNull="true">
<reportElement x="0" y="207" width="595" height="11" uuid="8d4dfe96-7bbf-48dd-99ae-b162a43a552f"/>
<textElement textAlignment="Center">
<font fontName="STXinwei" size="10" pdfFontName="STXinwei" pdfEncoding="Identity-H" isPdfEmbedded="true"/>
</textElement>
<textFieldExpression><![CDATA["第 "+$V{PAGE_NUMBER}+" 页,共 "+$V{PAGE_COUNT}+" 页"]]></textFieldExpression>
</textField>
</band>
</pageFooter>
</jasperReport>