带有OutputStream的JasperReport不能导出为PDF

时间:2017-06-23 18:24:53

标签: java jasper-reports

我使用JasperReport将报告导出为PDF。代码运行正常,控制台/日志中没有异常消息。但是,报告不会导出到浏览器。换句话说,正在创建报告,我无法下载或访问它。

这是导出代码:

public void generatePDFReport(Map<String, Object> parameters, JRDataSource jrDataSource, String resource, String filename)
{
    OutputStream os = null;
    try{
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
        os = response.getOutputStream();

        InputStream reportTemplate = this.getClass().getClassLoader().getResourceAsStream(resource);
        byte[] pdf = null;

        try {
            JasperDesign masterDesign = JRXmlLoader.load(reportTemplate);
            masterReport = JasperCompileManager.compileReport(masterDesign);
            masterReport.setWhenNoDataType(WhenNoDataTypeEnum.ALL_SECTIONS_NO_DETAIL);
            JasperPrint masterPrint = JasperFillManager.fillReport(masterReport, parameters, jrDataSource);
            pdf = JasperExportManager.exportReportToPdf(masterPrint);
        } catch (JRException e) {
            log.error(e);
        }
        response.setContentType("application/pdf");
        response.setContentLength(pdf.length);
        response.setHeader("Content-disposition", "attachment; filename=\""+filename+"\"");

        context.responseComplete();

        os.write(pdf);

        pdf = null;
    }catch(Exception e){
        log.error(e);
    }finally{
        try{
            os.flush();
            os.close();
        }catch(IOException e){
            log.error(e);
        }
    }
}

我几乎100%确定代码没有任何问题,因为它适用于不同的报告(我为其他几个报告运行相同的导出代码,除了这个报告之外,它对所有报告都有效)。

知道这一点,我认为它必须与报告本身有关。该报告是jrxml JasperReport文件。该报告是使用iReport创建的。但是,我修改了上面的代码,只是将其保存到下载文件夹,并且报告创建得非常好。

因此,问题是报告在后端成功创建,但未按预期发送到前端(浏览器)。

我愿意接受有关此报告无效的原因的任何建议。

2 个答案:

答案 0 :(得分:1)

在bean中运行代码是有问题的,因为:

  • 每个HTTP请求只允许一次调用getOutputStream
  • Web框架(J2EE / JSF)可能已经编写了HTTP标头
  • JSF页面可能已经作为HTML写入临时缓冲区(在调用responseComplete()时刷新)。
  • 标题可以重置,但这对getOutputStream问题
  • 没有帮助
  • 调用responseComplete()将任何HTML以及PDF内容刷新到浏览器

使用servlet。 servlet的send方法不需要比以下更复杂:

protected void send(final byte[] content) throws IOException {
    setContentLength(content.length);

    try (final OutputStream out = getOutputStream()) {
        out.write(content);
    }
}

还可以考虑设置缓存,以便无法进行陈旧的报告:

protected void disableCache() {
    // https://tools.ietf.org/html/rfc7234#section-7.1.3
    setHeader(CACHE_CONTROL, "private, no-store, no-cache, must-revalidate");

    // https://tools.ietf.org/html/rfc7234#section-5.3
    setHeader(EXPIRES, "Thu, 01 Dec 1994 16:00:00 GMT");

    // https://tools.ietf.org/html/rfc7234#section-5.4
    setHeader(PRAGMA, "no-cache");

    // https://tools.ietf.org/html/rfc7232#section-2.2
    setHeader(LAST_MODIFIED, getServerTimestamp());
}

private String getServerTimestamp() {
    final SimpleDateFormat rfc1123 = new SimpleDateFormat(DATE_RFC_1123, getDefault());

    rfc1123.setTimeZone(getTimeZone("GMT"));

    final Calendar calendar = Calendar.getInstance();
    return rfc1123.format(calendar.getTime());
}

这意味着,例如:

@WebServlet(
        name = "ReportServlet",
        urlPatterns = {PATH_SERVLET + "ReportServlet"}
)
public class ReportServlet extends AbstractServlet {
}

然后使用常规锚链接:

<h:outputLink value="/app/path/servlet/Reportservlet">Run Report</h:outputLink>

总之,不要通过拦截对JSF页面的请求来发送二进制报告数据;而是使用servlet。

servlet和JSF页面之间的通信可以通过以下方式进行:

  • 会话变量(servlet端的HTTPSession)
  • 网址参数

Servlet具有完全避免JSF开销的优势,从用户的角度来看,这将使报告运行得更快。另外,不要编译报告 - 直接使用.jasper文件,这也会提高性能。 (我并不是故意暗示使用.jrxml文件是问题,只是说这不是必要的步骤。)

答案 1 :(得分:1)

我找到了解决问题的方法。最终,我发现报告生成代码或报告没有任何问题,但是有一个ajax问题阻止了输出流将报告导出到浏览器。