报表在JavaWeb中是个非常常用的技术,本文介绍使用SpringBoot+AngularJS+Jaspersoft Studio对报表打印进行实现
一.使用Jaspersoft Studio进行报表的绘制后,存放在src/main/resources/reports文件夹下。
二.实现多张报表生成后台代码详解
此方法应用了JasperReport和JRAbstractExporter包对报表进行无脑化编译。
@RestController @RequestMapping(LevyDetailController.BASE_URL) public class LevyDetailController { public static Logger LOGGER = LoggerFactory.getLogger(LevyDetailController.class); /** 根路径 */ public final static String BASE_URL = "/reports/levy"; /** 报表打印服务接口类 */ @Resource private LevyDetailService levyDetailService; @SuppressWarnings("unchecked") @RequestMapping(method = RequestMethod.GET) public void printReport(@RequestParam("format") String format, @RequestParam("companyId") Long companyId, @RequestParam(required = false) Long issue, @RequestParam(required = false) Long pageIndex, HttpServletRequest request, HttpServletResponse response) { String requestFullUri = request.getRequestURI() + "?" + request.getQueryString(); OrganizationApplyService organizationApplyService = (OrganizationApplyService) SpringContextUtils .getApplicationContext().getBean("organizationApplyServiceImpl"); OrganizationApply company = organizationApplyService.findByOrgId(companyId); // 获取报表数据 CompanyLevyListDTO result = levyDetailService.getLevyDetail(company, issue); // result.setList(null);测试空报表 // 打印控制获取上下文 ApplicationContext ctx = SpringContextUtils.getApplicationContext(); org.springframework.core.io.Resource resource = null; if (result.getList() == null) { // 生成空白报表 resource = ctx.getResource("classpath:/reports/rptEmpty.jrxml"); } else { resource = ctx.getResource("classpath:/reports/rptLevyDetailx.jrxml"); } InputStream inputStream; try { // 译为输入流 inputStream = resource.getInputStream(); // 编译报表 JasperReport jasperReport = JasperCompileManager.compileReport(inputStream); JRAbstractExporter exporter = new JRPdfExporter(); List<JasperPrint> list = new ArrayList<>(); // 多张报表打印实现 if (result.getList() != null) { for (CompanyLevyDTO dto : result.getList()) { // 获取报表数据map源 JasperPrint jasperprint = JasperFillManager.fillReport(jasperReport, LevyDetaiFactorySolitary.getReportData(format, requestFullUri, dto, issue, company.getName(), company.getCompanyNumber()), LevyDetaiFactorySolitary.mapData(dto.getLevyBodyListDTO())); list.add(jasperprint); } } else { JasperPrint jasperprint = JasperFillManager.fillReport(jasperReport, (Map<String, Object>) DefaultEmptyFactory.getReportData(format, requestFullUri), DefaultEmptyFactory.mapData()); list.add(jasperprint); } // 设置前台编译格式 ServletOutputStream baos = response.getOutputStream(); exporter.setParameter(JRExporterParameter.JASPER_PRINT_LIST, list); exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos); exporter.setParameter(JRExporterParameter.CHARACTER_ENCODING, "UTF-8"); exporter.exportReport(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (JRException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
三.单张报表生成后台代码详解
此方法使用最基本SpringBooot response传入字节数组方式生成报表,代码如下:
@RestController @RequestMapping(LevyDetailController.BASE_URL) public class LevyDetailController {public static Logger LOGGER = LoggerFactory.getLogger(LevyDetailController.class);/** 根路径 */public final static String BASE_URL = "/reports/levy";/** 报表打印服务接口类 */@Resourceprivate LevyDetailService levyDetailService;@Autowiredprivate PileStamperClient pileStamperClient;@RequestMapping(method = RequestMethod.GET)public void printReport(@RequestParam("format") String format, @RequestParam("companyId") Long companyId, @RequestParam(required = false) Long issue, @RequestParam(required = false) Long pageIndex, HttpServletRequest request, HttpServletResponse response) throws JSONException { String requestFullUri = request.getRequestURI() + "?" + request.getQueryString(); OrganizationApplyService organizationApplyService = (OrganizationApplyService) SpringContextUtils .getApplicationContext().getBean("organizationApplyServiceImpl"); OrganizationApply company = organizationApplyService.findByOrgId(companyId); // 获取报表数据 CompanyLevyListDTO result = levyDetailService.getLevyDetail(company, issue); // result.setList(null);测试空报表 // 打印控制获取上下文 ApplicationContext ctx = SpringContextUtils.getApplicationContext(); org.springframework.core.io.Resource resource = null; if (result.getList() == null) { // 生成空白报表 resource = ctx.getResource("classpath:/reports/rptEmpty.jrxml"); } else { resource = ctx.getResource("classpath:/reports/rptLevyDetailx.jrxml"); } InputStream inputStream; try { byte[] bytesPdf = null; // 译为输入流 inputStream = resource.getInputStream(); // 编译报表 JasperReport jasperReport = JasperCompileManager.compileReport(inputStream); // 单张报表打印实现 if (result.getList() != null) { for (int t = 0; t < result.getList().size(); t++) { CompanyLevyDTO dto = result.getList().get(t); bytesPdf = JasperRunManager.runReportToPdf(jasperReport, LevyDetaiFactorySolitary.getReportData(format, requestFullUri, dto, issue, company.getName(), company.getCompanyNumber()), LevyDetaiFactorySolitary.mapData(dto.getLevyBodyListDTO())); } } else { bytesPdf = JasperRunManager.runReportToPdf(jasperReport, DefaultEmptyFactory.getReportData(format, requestFullUri), DefaultEmptyFactory.mapData()); } ByteArrayInputStream stream = new ByteArrayInputStream(bytesPdf); int length = stream.available(); if (length != -1) { byte[] buffer3 = new byte[1024]; int readCount = 0; ServletOutputStream sos = response.getOutputStream(); while ((readCount = stream.read(buffer3)) > 0) { sos.write(buffer3, 0, readCount); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (JRException e) { // TODO Auto-generated catch block e.printStackTrace(); }}}
五.使用PDFMergerUtility进行PDF字节流合成导出
mvn引入jar包
<dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox-app</artifactId> <version>1.8.13</version> </dependency>
@RestController@RequestMapping(LevyDetailController.BASE_URL)public class LevyDetailController { public static Logger LOGGER = LoggerFactory.getLogger(LevyDetailController.class); /** 根路径 */ public final static String BASE_URL = "/reports/levy"; /** 报表打印服务接口类 */ @Resource private LevyDetailService levyDetailService; @RequestMapping(method = RequestMethod.GET) public void printReport(@RequestParam("format") String format, @RequestParam("companyId") Long companyId, @RequestParam(required = false) Long issue, @RequestParam(required = false) Long pageIndex, HttpServletRequest request, HttpServletResponse response) { String requestFullUri = request.getRequestURI() + "?" + request.getQueryString(); OrganizationApplyService organizationApplyService = (OrganizationApplyService) SpringContextUtils .getApplicationContext().getBean("organizationApplyServiceImpl"); OrganizationApply company = organizationApplyService.findByOrgId(companyId); // 获取报表数据 CompanyLevyListDTO result = levyDetailService.getLevyDetail(company, issue); // result.setList(null);测试空报表 // 打印控制获取上下文 ApplicationContext ctx = SpringContextUtils.getApplicationContext(); org.springframework.core.io.Resource resource = null; if (result.getList() == null) { // 生成空白报表 resource = ctx.getResource("classpath:/reports/rptEmpty.jrxml"); } else { resource = ctx.getResource("classpath:/reports/rptLevyDetailx.jrxml"); } InputStream inputStream; try { byte[] bytesPdf = null; // 译为输入流 inputStream = resource.getInputStream(); // 编译报表 JasperReport jasperReport = JasperCompileManager.compileReport(inputStream); // 多张报表打印实现 if (result.getList() != null) { List<byte[]> list = new ArrayList<byte[]>(); for (CompanyLevyDTO dto : result.getList()) { // 获取报表数据map源 bytesPdf = JasperRunManager.runReportToPdf(jasperReport, LevyDetaiFactorySolitary.getReportData(format, requestFullUri, dto, issue, company.getName(), company.getCompanyNumber()), LevyDetaiFactorySolitary.mapData(dto.getLevyBodyListDTO())); list.add(bytesPdf); } if (result.getList().size() > 1) { // 多张表单拼接实现 PDFMergerUtility mergePdf = new PDFMergerUtility(); for (int i = 0; i < list.size(); i++) { byte[] content = list.get(i); ByteArrayInputStream in = new ByteArrayInputStream(content); mergePdf.addSource(in); } // 多个pdf流合成处理 mergePdf.setDestinationStream(new ByteArrayOutputStream()); try { mergePdf.mergeDocuments(); } catch (COSVisitorException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 获取合成后的OutputStream流 ByteArrayOutputStream outputStream = (ByteArrayOutputStream) mergePdf.getDestinationStream(); bytesPdf = outputStream.toByteArray(); } } else { bytesPdf = JasperRunManager.runReportToPdf(jasperReport, DefaultEmptyFactory.getReportData(format, requestFullUri), DefaultEmptyFactory.mapData()); } ByteArrayInputStream stream = new ByteArrayInputStream(bytesPdf); // 返回从输入流中读取的字节数,若无字节 返回0 int length = stream.available(); if (length != -1) { byte[] buffer3 = new byte[1024]; int readCount = 0; ServletOutputStream sos = response.getOutputStream(); while ((readCount = stream.read(buffer3)) > 0) { // 从buffer3中读取0-readCount的数据 sos.write(buffer3, 0, readCount); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (JRException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
四.LevyDetaiFactorySolitary实现,代码如下
public class LevyDetaiFactorySolitary { private LevyDetaiFactorySolitary() { } public static final String LEVY_REPORT_NAME = "rptLevyDetailx"; public static final String LEVY_FULL_URI = "request_full_uri"; public static String getReportName() { return LEVY_REPORT_NAME; } /** * 获取报表数据 * * @param format * @param requestFullUri * @param dto * @return */ public static Map<String, Object> getReportData(String format, String requestFullUri, CompanyLevyDTO dto, Long issue, String name, String companyNumber) { Map<String, Object> model = new HashMap<String, Object>(); model.put(JasperReportsMultiFormatView.DEFAULT_FORMAT_KEY, format); model.put(LEVY_FULL_URI, requestFullUri); model.put("name", "名称:" + name); ...//这里面放不是循环的参数 return model; } /** * 获取报表DataSource数据 * * @param format * @param requestFullUri * @param dto * @return */ public static JRMapCollectionDataSource mapData(List<LevyBodyDTO> list) { Collection<Map<String, ?>> collection = new ArrayList<Map<String, ?>>(); for (int i = 0; i < list.size(); i++) { Map<String, Object> map = new HashMap<String, Object>(); LevyBodyDTO dto = list.get(i); map.put("insuranceCode", dto.getInsuranceCode()); ...//这里面放循环的数据 collection.add(map); } return new JRMapCollectionDataSource(collection); }}
五.也可以使用直接返回前台ModelAndView的形式进行实现
代码入下:
ModelAndView module = null; if (listDimissionDTO.size() == 0) { // 生成报表 module = new ModelAndView(DefaultEmptyFactory.getReportName(), DefaultEmptyFactory.getReportData(format, requestFullUri)); } else { // 生成报表 module = new ModelAndView(LevyDetaiFactorySolitary .getReportName(), LevyDetaiFactorySolitary .getReportData(reportPageIndex, format, requestFullUri, listDimissionDTO, issue, company.getName(), company.getCompanyNumber(), reviewedStatus, submitType)); }
五.前端angularJS显示方案
目前市面上多种浏览器对报表的支持程度不一,最优解决方案如下:
ie十以上支持下载,谷歌火狐浏览器可在线预览
html:
<div class="form-group" style="margin-left:90px"> <input class="btn btn-success" type="button" style="width:100px;" value="打印报表" ng-click="report()"/> </div>
js
$scope.report = function () { if (window.navigator && window.navigator.msSaveOrOpenBlob) { $http({ url: $scope.URL + '?format=pdf&companyId=' + $scope.companyId + '&issue=' + $scope.issue, method: 'GET', responseType: 'arraybuffer' }).success(function (response) { var file = new Blob([response], {type: 'application/pdf'}); var fileURL = URL.createObjectURL(file); window.navigator.msSaveOrOpenBlob(file); }); } else { var reportOption = {}; reportOption.pdfName = $scope.title; reportOption.method = 'GET'; reportOption.url = $scope.URL + '?format=pdf&companyId=' + $scope.companyId + '&issue=' + $scope.issue; reportOption.headers = {Accept: '*/*'}; reportHelper.showReport(reportOption); } }; **五.从ftp服务器获取方案**
@RestController
@RequestMapping(ApplyPrintController.BASE_URL)
public class ApplyPrintController {
/** 根路径 */public final static String BASE_URL = "/reports/applyReport";@RequestMapping(method = RequestMethod.GET)public void printReport(@RequestParam("format") String format, @RequestParam(value = "companyId", required = true) String companyId, @RequestParam(value = "issue", required = false) Long issue, @RequestParam("buzzType") String buzzType, @RequestParam("submitType") String submitType, @RequestParam(value = "reviewedStatus", required = false) String reviewedStatus, @RequestParam(required = false) Long pageIndex, HttpServletRequest request, HttpServletResponse response) { String host = "10.154.227.46";// 设置服务器地址 int port = 21; // 设置服务器端口号 String username = "ftp2012";// 设置服务器访问用户 String password = "dfca@123";// 设置服务器密码 String remoteDir = "/ftpuser"; // 切换报表所在路径 /** 文件路径通配符 */ String regEx = "A20170020062.pdf"; String encoding = System.getProperty("file.encoding"); // 设置编码 FTPClient client = new FTPClient(); // 设置超时时间 client.setConnectTimeout(30000); try { // 1、连接服务器 if (!client.isConnected()) { // 如果采用默认端口,可以使用client.connect(host)的方式直接连接FTP服务器 client.connect(host, port); // 登录 client.login(username, password); // 获取ftp登录应答码 int reply = client.getReplyCode(); // 验证是否登陆成功 if (!FTPReply.isPositiveCompletion(reply)) { client.disconnect(); throw new RuntimeException("未连接到FTP,用户名或密码错误。"); } else { System.out.println("FTP连接成功。IP:" + host + "PORT:" + port); } // 2、设置连接属性 client.setControlEncoding(encoding); // 设置以二进制方式传输 client.setFileType(FTPClient.BINARY_FILE_TYPE); client.enterLocalPassiveMode(); } } catch (SocketException e) { try { client.disconnect(); } catch (IOException e1) { } throw new RuntimeException("连接FTP服务器失败" + e.getMessage()); } catch (IOException e) { } InputStream stream = null; try { // 1、设置远程FTP目录 client.changeWorkingDirectory(remoteDir); System.out.println("切换至工作目录【" + remoteDir + "】"); stream = client.retrieveFileStream(regEx); int length = stream.available(); if (length != -1) { byte[] buffer3 = new byte[1024]; int readCount = 0; ServletOutputStream sos = response.getOutputStream(); while ((readCount = stream.read(buffer3)) > 0) { sos.write(buffer3, 0, readCount); } } } catch (IOException e) { throw new RuntimeException("读取文件失败" + e.getMessage()); }}
}
联系客服