项目原因需要在springmvc的基础上整合jasperreports生成报表。其实springmvc已经提供了对jasperreports的支持,感觉springmvc采用的一个比较好的方式是将报表的生成作为一个view处理,但是需要对每一种报表配置他的jasperreports模板及视图的映射,这样的话添加报表必须变更配置,比较麻烦,所以自己想了一个方法来避免这种配置,代码可以很容易和spring整合起来。
japserreports生成报表基本流程其实就是根据一个模板和数据源生成一个中间类型,然后可以在此基础上可以导出几种格式。我的想法是提供方法供springmvc的controller调用产生中间文件,然后在view里面向客户端导出请求的格式。
首先是ReportPrint类,很简单,只是包含一个JasperPrint对象(既上述的中间文件),代码很简单,不解释
public class ReportPrint { JasperPrint jasperPrint = null; public JasperPrint getJasperPrint() { return jasperPrint; } public void setJasperPrint(JasperPrint jasperPrint) { this.jasperPrint = jasperPrint; } }接下来就是ReportCreater类,该类可通过spring注入到其他类中,调用它的createReport方法
public class ReportCreater { private static final Log logger = LogFactory.getLog(ReportCreater.class); private String jasperReportPath = null;//报表的模板文件存放路径(相对classpath,通过spring注入) /** * jasperDesignMap作为一个缓存来存储编译后的JasperReport模板 */ private Map<String, JasperReport> jasperDesignMap = new ConcurrentHashMap<String, JasperReport>(); public void resetJasperDesignCache() { jasperDesignMap.clear(); } /** * controller调用该方法来产生ReportPrint对象 */ public ReportPrint createReport(final String reportKey, final ResultSet rs, Map<String, ?> reportParams) throws ReportException { try { return _createReport(reportKey, rs, reportParams); } catch (JRException e) { logger.error(null, e); throw new ReportException("产生报表出错" + reportKey); } } private ReportPrint _createReport(final String reportKey, final ResultSet rs, Map<String, ?> reportParams) throws ReportException, JRException { JasperReport jasperReport = getJasperReport(reportKey); ReportPrint reportPrint = new ReportPrint(); JRResultSetDataSource resultSetDataSource = new JRResultSetDataSource(rs); JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, reportParams, resultSetDataSource); reportPrint.setJasperPrint(jasperPrint); return reportPrint; } private JasperReport getJasperReport(final String reportKey) { try { return _getJasperReport(reportKey); } catch (IOException e) { logger.error(null, e); throw new ReportException("关闭文件流异常:" + reportKey); } catch (JRException e) { logger.error(null, e); throw new ReportException("产生报表异常:" + reportKey); } } private JasperReport _getJasperReport(final String reportKey) throws IOException, JRException { JasperReport jasperReport = null; if (jasperDesignMap.containsKey(reportKey)) { jasperReport = jasperDesignMap.get(reportKey); } else { jasperReport = getJasperReportFromFile(final String reportKey); jasperDesignMap.put(reportKey, jasperReport); } return jasperReport; } /** * 从模板文件编译获得模板对象 */ private JasperReport getJasperReportFromFile(final String reportKey) throws IOException, JRException { String filePath = jasperReportPath + reportKey + ".jrxml";//图省事只支持jrxml的 InputStream jasperFileIS = null; JasperReport jasperReport = null; try { jasperFileIS = this.getClass().getClassLoader().getResourceAsStream(filePath); if (jasperFileIS == null) { throw new ReportException("报表文件不存在:" + filePath); } JasperDesign jasperDesign = JRXmlLoader.load(jasperFileIS); jasperReport = JasperCompileManager.compileReport(jasperDesign); } finally { if (jasperFileIS != null) { jasperFileIS.close(); } } return jasperReport; } public String getJasperReportPath() { return jasperReportPath; } public void setJasperReportPath(String jasperReportPath) { this.jasperReportPath = jasperReportPath; } public static void main(String[] argv) { } }
以上就可以产生中间文件了,接下来就是按照spring的view规范写一个导出各种格式的视图就可以了
public class ReportView extends AbstractView { private static final Log logger = LogFactory.getLog(ReportView.class); private static final String XLS = "xls"; private static final String PDF = "pdf"; private static final String CSV = "csv"; private static final String REPORT_NAME = "reportName"; private static final String FORMAT = "format"; private static final String REPORT_PRINT = "reportPrint"; private static final String HTML = "html"; private static Map<String, IReportFileExporter> EXPORTER_MAP = new HashMap<String, IReportFileExporter>(4); static { EXPORTER_MAP.put(XLS, new ReportXlsExporter()); EXPORTER_MAP.put(PDF, new ReportPdfExporter()); EXPORTER_MAP.put(CSV, new ReportCsvExporter()); EXPORTER_MAP.put(HTML, new ReportHtmlExporter()); } @Override protected void renderMergedOutputModel(Map model, HttpServletRequest request, HttpServletResponse response) { String reportName = (String) model.get(REPORT_NAME);//报表的文件名 String format = (String) model.get(FORMAT);//报表的格式pdf xls ..... ReportPrint reportPrint = (ReportPrint) model.get(REPORT_PRINT);//这就是之前生成的中间文件 response.setContentType("application/x-msdown;charset=utf-8"); try { /* http头里的文件名貌似不支持utf-8,gbk之类的编码,需要转换一下 * 另外发现如果用new String(reportName.getBytes("UTF-8"), "iso-8859-1")的话Chrome和FF的 * 下载对话框的文件名是正常的,IE却是乱码,只能用GBK才正常 */ response.setHeader("Content-Disposition","attachment;filename=\"" + new String(reportName.getBytes("GBK"), "iso-8859-1") + "\""); } catch (UnsupportedEncodingException e) { logger.error(null, e); } exportFile(reportPrint, format, response); } private void exportFile(ReportPrint reportPrint, String format, HttpServletResponse response) { try { _exportFile(reportPrint, format, response); } catch (JRException e) { logger.error("导出报表异常", e); } catch (IOException e) { logger.error(null, e); } } private void _exportFile(ReportPrint reportPrint, String format, HttpServletResponse response) throws IOException, JRException { OutputStream buffOS = null; try { buffOS = new BufferedOutputStream(response.getOutputStream()); IReportFileExporter exporter = null; if (EXPORTER_MAP.containsKey(format)) { exporter = EXPORTER_MAP.get(format);//获取需要格式的导出类 exporter.export(reportPrint, buffOS); } else { logger.error("错误的报表格式:" + format); } } finally { if (buffOS != null) { buffOS.close(); } } } }导出器是一个简单的接口,各种格式只要实现export方法就可以了
public interface IReportFileExporter { public void export(ReportPrint reportPrint, OutputStream os) throws JRException; }给一个导出PDF格式的例子,很简单
public class ReportPdfExporter implements IReportFileExporter { public void export(ReportPrint reportPrint, OutputStream os) throws JRException { JRPdfExporter exporter = new JRPdfExporter(); exporter.setParameter(JRExporterParameter.JASPER_PRINT, reportPrint.getJasperPrint()); exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, os); exporter.exportReport(); } }
差不多久这样了,完整的代码已经上传。