项目中需要生成PDF和Word文件的报告,文件中包含图片和表格。基于Java的解决方案有Freemarker模板引擎,是通过XML文件将填入的内容放上${}占位符。这种方式对于简单的文本是没问题,但如果占位符中有字符跟${}冲突,就比较难处理了。
iReport是一款可视化报表设计工具,看软件界面跟Qt的风格有几分相似,内置丰富的图表,能够创建复杂的报表。相比XML模板的方式,更加灵活和稳定。
- 主界面
本文以一个实际的导出PDF报告案例来讲解。
一、ireport可视化布局
1,插入图片
从组件面板拖动Image到中间的页面Designer区域。
在右下角的Image属性列表中,设置Image Expression为$F{netImage}。
不过,首先得在Fields定义好变量:netImage,netImage可以在Java工程中传值传过来,也可以是静态图片或url。
Image Expression支持的三种方式:
- $F{netImage};
- “D://net.png”
- “https://dn-abc.qbox.me/logo.png"
生成的pdf效果:
2,插入表格
为了便于扩展,表格放到Subreport中;在组件面板中新建一个Subreport,进入subreport设计表格;
这里有几个要点:
a, 中文字符的显示:
需要在属性中设置
1 2 3
| Pdf Font: STSong-Light Pdf Embedded: 勾选 Pdf Encoding: UniGB-UCS2-H(Chinese Simplified)
|
b, 数字的格式化
由于Java传过来的值在ireport中默认都是String,在Expression Class中设置为java.lang.Double也没有用。
需要在Text Field Expression中将字符串转成Double,然后在格式化,Pattern中设置“###0.0000”,表示保留4为小数。
c, 数字百分比的显示
1 2
| Text Field Expression: ($F{val}.equals("") ? "--" : new Double($F{val})) Pattern:#,##0.00%
|
如果val为空,则用显示–。按百分比格式化,保留2位小数。
效果如下:
3,生成编译文件
ireport源文件是jrxml格式,点击下图中的锤子,编译后生成jasper文件。
二、Java项目配置
将生成的jasper文件导入到java工程
1,生成图片
项目中的方案是从echarts中截图,将截图保存到本地文件夹
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
| /** * 调用js * 产生图片 */ function genPic(callback){ var data = "pic="+encodeURIComponent(mainChart.getDataURL( { type:"png", pixelRatio:1, excludeComponents:['toolbox', 'dataZoom'] })); var xmlhttp; if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari xmlhttp = new XMLHttpRequest(); } else { // code for IE6, IE5 xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.open("POST",ctx+"/productReport/genNetPic?fundId="+$('#netfundId').val(),true); xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { // 回调生成结果 callback(true); } else if (xmlhttp.status!=200){ callback(false); } } xmlhttp.send(data); }
|
Controller中的函数:
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
| @RequestMapping(value = "genNetPic", method = RequestMethod.POST) @ResponseBody public BaseResponse genNetPic(HttpServletRequest request){ logger.info("开始生成图片"); String pic = request.getParameter("pic"); String fundId = request.getParameter("fundId"); BaseResponse baseResponse = null; try{ String[] url = pic.split(","); String u = url[1]; byte[] b = new BASE64Decoder().decodeBuffer(u); // 创建图片生成的路径:日期+产品id Date curDate = new Date(); StringBuilder filePath = new StringBuilder(""); filePath.append(upload_path).append(DateUtil.formatDate(curDate, "yyyy-MM-dd")).append("/").append(fundId); FileUtil.createDictionary(filePath.toString()); // 创建图片文件 OutputStream out = new FileOutputStream(new File(filePath.toString()+"/net.png")); out.write(b); out.flush(); out.close(); baseResponse = new BaseResponse(true); logger.info("完成生成图片"); } catch(Exception e){ logger.error("生成图片失败,失败原因:{}",e.getMessage()); baseResponse = new BaseResponse(false, "图片生成失败"); } return baseResponse; }
|
2,构建报告参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| /** * 构建报表的参数 * @return */ private FundReportDto getReportParams(String fundId, String fundName, Date curDate){ String dateStr = DateUtil.formatDate(curDate, "yyyy-MM-dd"); // FundReportDto fundReportDto = new FundReportDto(); // 得到图片路径 StringBuilder netImage = new StringBuilder(jasper_chart_path); netImage.append(dateStr).append("/").append(fundId).append("/").append("net.png"); fundReportDto.setNetImage(netImage.toString()); ...
return fundReportDto; }
|
返回参数用对象FundReportDto封装,FundReportDto是可序列化的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import java.io.Serializable; import java.util.ArrayList; import java.util.List;
public class FundReportDto implements Serializable{ /** * */ private static final long serialVersionUID = -3023588547102105811L; /** * 统计日期 */ private String statisticDate; /* * 图片 */ private String netImage; ... }
|
3,生成报告
报告支持pdf和word两种格式,生成步骤是先将报告保存在本地文件夹,然后调用浏览器下载。
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
| /** * 一建生成报告 */ import net.sf.jasperreports.engine.JRExporterParameter; 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 net.sf.jasperreports.engine.export.JRRtfExporter; import net.sf.jasperreports.engine.util.JRLoader;
@RequestMapping(value = "exportReport", method = RequestMethod.GET) public void exportReport(String fundId, String fundName, String fileFormat, HttpServletResponse response){ Date curDate = new Date(); String dateStr = DateUtil.formatDate(curDate, "yyyy-MM-dd"); // 文件路径 StringBuilder filePath = new StringBuilder(""); filePath.append(upload_path).append(dateStr).append("/").append(fundId); FileUtil.createDictionary(filePath.toString()); // 文件名 StringBuilder fileName = new StringBuilder(""); fileName.append(fundName).append("评价报告_").append(dateStr).append(".").append(fileFormat); // 文件全路径 StringBuilder fullName = new StringBuilder(""); fullName.append(filePath).append("/").append(fileName); // 加载报表对象 InputStream is = null; JasperReport jasperReport = null; JasperPrint jasperPrint = null; is = ProductDetailController.class.getClassLoader().getResourceAsStream(analysis_jasper_path); try{ // 响应头部信息设置 response.setContentType("text/plain"); response.setHeader("Content-Disposition", "attachment; filename="+FileUtil.getAttachName(fileName.toString())); // jasperReport = (JasperReport) JRLoader.loadObject(is); Map<String,Object> params = new HashMap<String,Object>(); params.put("SUBREPORT_DIR", getClassPath(subreport_dir)); params.put("fundName", fundName); params.put("reportTitle", fundName+"报告"); // List<FundReportDto> fundReportDtos = new ArrayList<FundReportDto>(); FundReportDto fundReportDto = getReportParams(fundId, fundName,curDate); fundReportDtos.add(fundReportDto); params.put("statisticDate", fundReportDto.getStatisticDate()); // jasperPrint = JasperFillManager.fillReport(jasperReport, params, new JRBeanCollectionDataSource(fundReportDtos)); if (fileFormat.equals("pdf")) { JasperExportManager.exportReportToPdfFile(jasperPrint, fullName.toString()); }else{ JRRtfExporter docReport = new JRRtfExporter(); docReport.setParameter(JRExporterParameter.OUTPUT_FILE_NAME,fullName.toString()); docReport.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint); docReport.exportReport(); } FileUtil.download(fullName.toString(), response.getOutputStream()); } catch(Exception e){ e.printStackTrace(); logger.error("生成报告失败,失败原因:{}",e.getMessage()); } finally{ try{ if (is!=null) is.close(); }catch(Exception e){ logger.error("关闭文件输入流异常:{}",e.getMessage()); } } }
|
其中analysis_jasper_path为jasper文件的路径。
这样,一份完整的pdf或word报告就制作出来了。