用JasperReport+iReport進(jìn)行Web報表開發(fā)
序言
在很多實際的項目里,報表都是其中十分重要的組成部分,例如把查詢結(jié)果以報表的形式呈現(xiàn)出來。這里所提到的報表可不是簡單的二維表,而是擁有復(fù)雜表頭的、多維的、可以在運行期從數(shù)據(jù)庫中自動讀取數(shù)據(jù)、可自動分頁、擁有豐富的頁面元素(圖片,超連接等)、支持分組和交叉表、支持打印、最好還能導(dǎo)出到Excel或Word…...(汗
L)。但是顯而易見,報表功能越強大,提供的服務(wù)越豐富,其復(fù)雜度也就越提高,所以僅靠石器時代的手工方式生成報表是不能滿足需要的。所幸,目前我們所熟知的幾款報表工具功能上足夠強大,而且都附有很方便的報表生成工具。它們分別是:JasperReport(+iReport),BIRT(+eclipse),水晶報表(+eclipse,JBuiler等等)。
之所以提到這三種報表工具首先是因為他們都是開放源碼的(CrystalReportForEclipse1.0已經(jīng)開源了)。既然不用考慮費用,那在我們的項目中到底選用哪一個呢?對于水晶報表而言,雖然其在.Net平臺上表現(xiàn)十分搶眼,但是在Java平臺上,多數(shù)的實現(xiàn)都是要收費的(例如For JBuilder版),而且其Eclipse插件的資源消耗十分驚人(我的機器配置為P
4 3.0+512RAM,使用“Eclipse3.2+水晶報表插件”根本就跑不動)。所以我選擇了純Java的報表工具JasperReport與iReport的組合。但是關(guān)于JasperReport的文檔相對匱乏,其官方文檔還是要收費的,所我希望利用這篇文章展示如何利用這一強力組合來進(jìn)行基于Web的報表開發(fā),希望能為那些苦于報表的同仁們解決一些實際問題。
本文將火力集中在如何在Web環(huán)境下配置和使用JasperReport報表和報表的導(dǎo)出功能等方面,由于在以前的Blog中我已經(jīng)寫過如何設(shè)計普通的報表,所里這里將不再贅述。對于那些基本的操作則留給讀者自行體會,相信在iReport的幫助下,上手會很快的。
(注:本文已被《程序員》收錄,未經(jīng)允許不得轉(zhuǎn)載
)
1
JasperReport簡介
2
Web報表開發(fā)
2.1
環(huán)境設(shè)置
2.2
報表預(yù)覽框架
2.3
使用JNLP技術(shù)實現(xiàn)客戶端預(yù)覽
3
結(jié)束語... 24
1 JasperReport簡介
JasperReport是一個強大、靈活的報表生成工具,能夠展示豐富的頁面內(nèi)容,并將之轉(zhuǎn)換成PDF,HTML,XML,Excel(通過POI或JExcelAPI實現(xiàn))和Rtf(通過POI實現(xiàn))格式。該庫完全由Java寫成,可以用于在各種Java應(yīng)用程序,包括J2EE,Web應(yīng)用程序中生成動態(tài)內(nèi)容。它的主要目的是輔助生成面向頁面的(page oriented),準(zhǔn)備付諸打印的文檔。JasperReport借由定義于XML文檔中的report design進(jìn)行數(shù)據(jù)組織。這些數(shù)據(jù)可能來自不同的數(shù)據(jù)源,包括關(guān)系型數(shù)據(jù)庫,collections,java對象數(shù)組。通過實現(xiàn)簡單的接口,用戶就可以將report library插入到訂制好的數(shù)據(jù)源中。用JasperReport進(jìn)行報表開發(fā)的過程如下所示(Version=1.0):

目前JasperReport最新的版本是1.2.7,可以到Sourceforg網(wǎng)站下載其整個工程及代碼。其工程文件目錄下的demo子目錄中包含很多定義良好的例子,可以實現(xiàn)各種所需功能。鑒于它的文檔收費,想學(xué)習(xí)使用JasperReport的話我們也只能以這些demo作為學(xué)習(xí)資料了。
但是繁瑣的XML標(biāo)記和功能API在提供強大的動態(tài)及可擴展開發(fā)的同時也帶來了超高的復(fù)雜性,在沒有免費文檔的情況下,手工編寫報表設(shè)計所需的XML文件是極其不明智的。不過正如我們用JBuilder(或其他可視化開發(fā)工具)編寫SwingGUI時一樣,我們可以采用iReport進(jìn)行可視化的報表設(shè)計來避免和可怕的XML文件及實現(xiàn)細(xì)節(jié)打交道。雖然可能會損失一些動態(tài)生成報表的靈活性,但是大多數(shù)情況下,我們只需要靜態(tài)的設(shè)計框架和動態(tài)的裝填數(shù)據(jù)而很少需要動態(tài)的報表框架,所以和我們所獲得的方便相比,這些小小的損失簡直可以忽略不計了。當(dāng)然如果確實需要,且看到下面的東西你不暈的話,自己動手確實可以獲得所需的靈活性。
其中的VerticalFilling和HorizontalFilling表示裝填數(shù)據(jù)的順序。從上圖我們可以清楚地看到,一個報表的設(shè)計主要由PageHeader和報表內(nèi)容組成,報表內(nèi)容又是由列組成,內(nèi)容既可以是一列也可以是多列,還可以是Group。具體的實例如下:

這些元素到底在JaserReport的XML設(shè)計文件中的定義為何我并不想關(guān)心,因為這都由iReport負(fù)責(zé)操心了,我們只需輕松的像搭積木一樣利用iReport添加各種可視化元素就可以了。相信用過之后你會對iReport愛不釋手,就像我一樣。出于實際需要,我會提供一個簡單的動態(tài)表單的生成框架供各位參考。
2 Web報表開發(fā)
現(xiàn)今的環(huán)境是Web大行其道,一個工具如果不能融入Web功能就無法立足。JasperReport的開發(fā)者顯然很早就意識到了這一點,所以在JasperReport1.0以前就加入了支持Servlet/JSP的能力。也就是說,我們可以利用Servlet/JSP將生成好的報表導(dǎo)出成HTML(或PDF/RTF/EXCEL)格式供預(yù)覽或?qū)С鲋?。然而唯一的缺憾在于JasperReport并未提供在客戶端直接打印的功能,而除了使用Applet之外我們又不能直接在客戶端顯示JRViewer這樣的預(yù)覽窗口,如何解決這些問題呢?
2.1環(huán)境設(shè)置
在Servlet/JSP中使用JasperReport無需更多的設(shè)置,只需要將JasperReport所用到的jar包放入工程中的WEB-INF/lib目錄下即可。在程序運行期,Servlet/JSP只需能夠正確加載報表文件,裝填數(shù)據(jù)并生成JasperPring對象,利用我下面給出的導(dǎo)出框架稍加修改即可生成一個帶有HTML/PDF/RTF/EXCEL導(dǎo)出功能以及可將HTML預(yù)覽進(jìn)行分頁的功能模塊。
2.2報表預(yù)覽框架
<%@page contentType="text/html; charset=UTF-8"%>
<%@page import="javax.servlet.*"%>
<%@ page import="net.sf.jasperreports.engine.*" %>
<%@ page import="net.sf.jasperreports.engine.util.*" %>
<%@ page import="net.sf.jasperreports.engine.export.*" %>
<%@ page import="net.sf.jasperreports.j2ee.servlets.*" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%
JasperPrint jasperPrint = (JasperPrint)session.getAttribute("JasperPrint");
session.setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_SESSION_ATTRIBUTE,jasperPrint);
String pageTitle = (String)session.getAttribute("pageTitle");
JRHtmlExporter exporter = new JRHtmlExporter();
int pageIndex = 0;
int lastPageIndex = 0;
if (jasperPrint.getPages() != null){
lastPageIndex = jasperPrint.getPages().size() - 1;
}
String pageStr = request.getParameter("pageIndex");
try{
if( pageStr != null)
pageIndex = Integer.parseInt(pageStr);
}catch(Exception e){
//e.printStackTrace();
}
if (pageIndex < 0){
pageIndex = 0;
}
if (pageIndex > lastPageIndex){
pageIndex = lastPageIndex;
}
StringBuffer sbuffer = new StringBuffer();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_STRING_BUFFER, sbuffer);
exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, "ImageServlet?image=");
exporter.setParameter(JRExporterParameter.PAGE_INDEX, new Integer(pageIndex));
exporter.setParameter(JRHtmlExporterParameter.HTML_HEADER, "");
exporter.setParameter(JRHtmlExporterParameter.BETWEEN_PAGES_HTML, "");
exporter.setParameter(JRHtmlExporterParameter.HTML_FOOTER, "");
try{
exporter.exportReport();
}catch(Exception e){
e.printStackTrace();
}
%>
這部分代碼用于將Servlet生成的JasperReport對象導(dǎo)出成HTML格式,導(dǎo)出所用的Servlet為JasperReport自帶的ImageServlet。要特別注意的是我加了顏色部分的代碼,即一定要向Session變量中放入一個JasperPrint對象,其關(guān)鍵字為“
ImageServlet.DEFAULT_JASPER_PRINT_SESSION_ATTRIBUTE”,這樣ImageServlet就可以獲取并自動導(dǎo)出報表了。
<html>
<head>
<title><%=pageTitle %></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="CSS/style.css">
</head>
<body>
<table class="titleBarT">
<tr>
<td> >>
<%=pageTitle %>
</td>
</tr>
</table>
<table width="98%" cellpadding="0" cellspacing="0" border="0" height="22">
<tr>
<td>
<div class="menu"><a href="PdfServlet"><img src="Images/pdf.gif" border="0"></a></div>
<div class="menu"><a href="RtfServlet"><img src="Images/word.gif" border="0"></a></div>
<div class="menu"><a href="XlsServlet"><img src="Images/excel.gif" border="0"></a></div>
<div class="menu"><a href=""> </a></div>
<div>
<%
if (pageIndex > 0)
{
%>
<a href="本頁?pageIndex=0"><img src="Images/FirstPage.gif" border="0"></a>
<a href="本頁?pageIndex=<%=pageIndex - 1%>"><img src="Images/PreviousPage.gif" ></a>
<%
}
else
{
%>
<img src="Images/FirstPage_disabled.gif" border="0"/>
<img src="Images/PreviousPage_disabled.gif" border="0"/>
<%
}
if (pageIndex < lastPageIndex)
{
%>
<a href="本頁?pageIndex=<%=pageIndex + 1%>"><img src="Images/NextPage.gif" ></a>
<a href="本頁?pageIndex=<%=lastPageIndex%>"><img src="Images/LastPage.gif" ></a>
<%
}
else
{
%>
<img src="Images/NextPage_disabled.gif" border="0">
<img src="Images/LastPage_disabled.gif" border="0">
<%
}
%>
</div>
</td>
</tr>
</table>
這段代碼是將導(dǎo)出成
HTML
的報表進(jìn)行分頁顯示。
<table width="98%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td width="50%"> </td>
<td align="left">
<%=sbuffer.toString()%>
</td>
<td width="50%"> </td>
</tr>
</table>
</body>
</html>
這里導(dǎo)出報表內(nèi)容的代碼。用Tomcat作為WebContainer,顯示的結(jié)果如下:

利用這個框架我們可以輕易的實現(xiàn)自動分頁的功能并將報表導(dǎo)出成我們想要的格式:如PDF,Word,Excel的等。

限于篇幅,這里我不能夠展現(xiàn)報表開發(fā)的每一個細(xì)節(jié)和過程,但是我已經(jīng)盡量將Web報表開發(fā)的大概過程提取出來,并著重介紹數(shù)據(jù)源的設(shè)制,交叉表的設(shè)計,以及Web預(yù)覽框架這些相信每個做Web報表的人都會遇到的問題及其解決方案,在JasperReport的高端使用文檔相對匱乏的情況下,希望我的努力能給你帶來一點幫助。