如何在Spring MVC中使用JasperReports?

41

我一直在研究如何使用JasperReports(6.0.0)和Spring MVC(4.1.3)生成PDF报告。Spring提供了许多“Spring特定”的集成方式,用于生成PDF报告:

我很难在网上找到好的、完整的示例,因此想分享我的发现(请参见下面的答案)。

欢迎添加其他与“如何将JasperReports与Spring4集成?”相关的方法和/或改进。


这是在Spring MVC项目中使用Jasper报告的示例:https://github.com/dihaw-team/dihaw-spring-jasperreports - Wahid Gazzah
2个回答

83

根据我的研究,我发现以下使用方法。这些方法从最直接(天真)的方法开始,涉及更少的前端复杂性/配置,并发展成为更抽象的方法,但对Spring有更多的依赖/更复杂的Spring配置。

方法1:在控制器中直接使用JasperReports API

只需将内容写入servlet输出流即可。

  @RequestMapping(value = "helloReport1", method = RequestMethod.GET)
  @ResponseBody
  public void getRpt1(HttpServletResponse response) throws JRException, IOException {
    InputStream jasperStream = this.getClass().getResourceAsStream("/jasperreports/HelloWorld1.jasper");
    Map<String,Object> params = new HashMap<>();
    JasperReport jasperReport = (JasperReport) JRLoader.loadObject(jasperStream);
    JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, new JREmptyDataSource());

    response.setContentType("application/x-pdf");
    response.setHeader("Content-disposition", "inline; filename=helloWorldReport.pdf");

    final OutputStream outStream = response.getOutputStream();
    JasperExportManager.exportReportToPdfStream(jasperPrint, outStream);
  }

方法二:将JasperReportPdf视图注入到控制器中

假设已有JasperReportsPdfView bean:


@Bean @Qualifier("helloWorldReport2")
public JasperReportsPdfView getHelloWorldReport() {
  JasperReportsPdfView v = new JasperReportsPdfView();
  v.setUrl("classpath:jasperreports/HelloWorld2.jasper");
  v.setReportDataKey("datasource");
  return v;
}

这个视图可以被注入或者连接到控制器中进行使用:

@Autowired @Qualifier("helloWorldReport2")
private JasperReportsPdfView helloReport;

@RequestMapping(value = "helloReport2", method = RequestMethod.GET)
public ModelAndView getRpt2(ModelAndView modelAndView) {
  Map<String, Object> parameterMap = new HashMap<>();
  parameterMap.put("datasource", new JREmptyDataSource());
  modelAndView = new ModelAndView(helloReport, parameterMap);
  return modelAndView;
}

请注意,使用JasperReportsPdfView(或更加灵活的JasperReportsMultiFormatView)需要依赖于spring-context-support:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>4.1.3</version>
</dependency>

方法三:使用XML或ResourceBundle视图解析器将逻辑视图名称映射到JasperReport视图

配置一个新的视图解析器,例如ResourceBundleViewResolver,在InternalResourceViewResolver之前运行。这基于设置的顺序值(0先于1):

@Bean
public ResourceBundleViewResolver getResourceBundleViewResolver() {
  ResourceBundleViewResolver resolver = new ResourceBundleViewResolver();
  resolver.setBasename("jasperreport-views");
  resolver.setOrder(0);
  return resolver;
}

@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
  InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/views/");
  resolver.setSuffix(".jsp");
  resolver.setOrder(1);
  return resolver;
}

然后,在我们的类路径的根目录下,jasperreport-views.properties 文件可以包含逻辑视图名称与相关类和属性值(即url和reportDataKey)配对,以便呈现JasperReport:

helloReport3.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
helloReport3.url=classpath:/jasperreports/HelloWorld3.jasper
helloReport3.reportDataKey=myDataSourceKey

控制器代码如下所示:

@RequestMapping(value = "helloReport3", method = RequestMethod.GET)
public ModelAndView getRpt3(ModelMap modelMap, ModelAndView modelAndView) {
  modelMap.put("myDataSourceKey", new JREmptyDataSource());
  return new ModelAndView("helloReport3", modelMap);
}

我喜欢这种方法。控制器保持“愚笨”,只与字符串值打交道,名称到视图的映射可以在一个位置完成。


方法4:使用JasperReportsViewResolver

配置一个零序的JasperReportViewResolver,关键是使用setViewNames来告诉Spring您希望这个解析器处理哪些逻辑视图名称(否则您将遇到“无法从类路径资源[jasperreports / index.jasper]”类型错误):

@Bean
public JasperReportsViewResolver getJasperReportsViewResolver() {
  JasperReportsViewResolver resolver = new JasperReportsViewResolver();
  resolver.setPrefix("classpath:/jasperreports/");
  resolver.setSuffix(".jasper");
  resolver.setReportDataKey("datasource");
  resolver.setViewNames("rpt_*");
  resolver.setViewClass(JasperReportsMultiFormatView.class);
  resolver.setOrder(0);
  return resolver;
}  

@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
  InternalResourceViewResolver resolver = new InternalResourceViewResolver();
  resolver.setPrefix("/WEB-INF/views/");
  resolver.setSuffix(".jsp");
  resolver.setOrder(1);
  return resolver;
}

在控制器中:

@RequestMapping(value = "helloReport4", method = RequestMethod.GET)
public ModelAndView getRpt4(ModelMap modelMap, ModelAndView modelAndView) {
  modelMap.put("datasource", getWidgets());
  modelMap.put("format", "pdf");
  modelAndView = new ModelAndView("rpt_HelloWorld", modelMap);
  return modelAndView;
}

这是我首选的方法。控制器解析jasper报表与使用InternalResourceViewResolver解析jsp视图非常相似,因此不需要像以上第3种方法中使用xml或属性文件一样进行显式映射。

编辑

JasperReportsPdfView的Java文档提到它使用已弃用的JRExporter API。有没有更好(更新)的JasperReports视图可供使用?也许选择JasperReportsMultiFormatView是更好的选择,因为它似乎没有使用JRExporter


1
这个样例应用程序对我帮助很大,我使用相同的方法在这里创建了一个 https://github.com/gauravbrills/jasperreportswithspringboot - Gaurav Rawat
我已经尝试了XLS的第四种方法,它有效。但是生成的文件没有扩展名。如何使用第四种方法设置生成文件的文件名和扩展名? - tranceholic
@Brice Ronance:你能帮我吗?我在查看我的JasperReport时卡住了。http://stackoverflow.com/q/38390723/2666368 - Ke Vin
@Brice,你能告诉我在这里如何使用“REPORT_VIRTUALIZER”吗? - Swap L
感谢@Brice Roncace的详细解释。我正在使用第三种方法,但总是得到一个空的PDF文件。检查日志后发现,从BeanNameViewResolver:74中找不到modelview:'blue'视图名称没有匹配的bean。你有什么建议吗?谢谢。 - drenda
@Brice 我正在使用方法1,但我的报告没有显示出来。服务器或浏览器控制台中没有错误日志。我可以在Mozilla浏览器的Firebug响应选项卡中看到PDF已经成功返回。但它没有在浏览器中显示。 - raikumardipak

2

我的方法:

   @RequestMapping(value="getPDF", method=RequestMethod.GET)
   public void generatePDF(int idPredstave, HttpServletResponse response) throws Exception{

    Predstava p = pr.findById(idPredstave).get();
    List<Uloga> uloge = ur.findByPredstava(p);

    response.setContentType("text/html");
    JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(uloge);
    InputStream inputStream = this.getClass().getResourceAsStream("/jasperreports/Uloge.jrxml");
    JasperReport jasperReport = JasperCompileManager.compileReport(inputStream);
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("nazivPredstave", p.getNaziv());
    params.put("trajanje", p.getTrajanje());
    params.put("opis", p.getOpis());
    params.put("zanr", p.getZanr().getNaziv());
    params.put("reziser", p.getReziser().getIme()+" "+p.getReziser().getPrezime());
    JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, dataSource);
    inputStream.close();


    response.setContentType("application/x-download");
    response.addHeader("Content-disposition", "attachment; filename=Uloge.pdf");
    OutputStream out = response.getOutputStream();
    JasperExportManager.exportReportToPdfStream(jasperPrint,out);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接