简体   繁体   English

Jasper Reports以xlsx,docx,csv,rtf等格式打印未配置的报告

[英]Jasper Reports print unconfigured report in xlsx, docx, csv, rtf, etc

A web application prints PDF reports without problems, but when reports in xlsx, docx, csv, rtf, etc. are all not configured correctly. Web应用程序可以毫无问题地打印PDF报告,但是如果xlsx,docx,csv,rtf等报告均未正确配置。 The browser tries to save the file always with the .xhtml extension. 浏览器将尝试始终以.xhtml扩展名保存文件。

How do you export the report to a browser such that the file exports with the correct filename and media type? 您如何将报告导出到浏览器,以便使用正确的文件名和媒体类型导出文件?

Code: 码:

public void gerarJasper(String name, String type, List data, Map params) throws IllegalArgumentException, RuntimeException, Exception {

    boolean found = false;
    for (int i = 0; i < VALID_TYPES.length; i++) {
        if (VALID_TYPES[i].equals(type)) {
            found = true;
            break;
        }
    }
    if (!found) {
        throw new IllegalArgumentException("Tipo solicitado '" + type + "' inválido");
    }

    // Procurar recurso de design de relatório compilado
    ExternalContext econtext = FacesContext.getCurrentInstance().getExternalContext();

    InputStream stream = econtext.getResourceAsStream(PREFIX + name + SUFFIX);
    if (stream == null) {
        throw new IllegalArgumentException("O relatório '" + name + "' não existe");
    }

    FacesContext fc = FacesContext.getCurrentInstance();
    ServletContext context = (ServletContext)fc.getExternalContext().getContext();
    String path = context.getRealPath(File.separator) + "resources/jasper" + File.separator;
    String logo = context.getRealPath(File.separator) + "resources/imagens" + File.separator;
    params.put("SUBREPORT_DIR", path);
    params.put("LOGO_DIR", logo);                

    JRDataSource ds = new JRBeanArrayDataSource(data.toArray());
    JasperPrint jasperPrint = null;
    try {
        jasperPrint = JasperFillManager.fillReport(stream, params, ds);
    } catch (RuntimeException e) {
        throw e;
    } catch (Exception e) {
        throw new FacesException(e);
    } finally {
        try {
            stream.close();
        } catch (IOException e) {
        }
    }

    JRExporter exporter = null;
    HttpServletResponse response = (HttpServletResponse) econtext.getResponse();
    FacesContext fcontext = FacesContext.getCurrentInstance();
    try {
        response.setContentType(type);
        if ("application/pdf".equals(type)) {
            exporter = new JRPdfExporter();
            exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
            exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, response.getOutputStream());
        } else if ("text/html".equals(type)) {
            exporter = new JRHtmlExporter();
            exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
            exporter.setParameter(JRExporterParameter.OUTPUT_WRITER, response.getWriter());
            // Tornar imagens disponíveis para a saída HTML
            HttpServletRequest request = (HttpServletRequest) fcontext.getExternalContext().getRequest();
            request.getSession().setAttribute(ImageServlet.DEFAULT_JASPER_PRINT_SESSION_ATTRIBUTE, jasperPrint);
            exporter.setParameter(JRHtmlExporterParameter.IMAGES_MAP, new HashMap());
            // A seguinte instrução requer mapeamento / imagem
            // para o imageServlet no web.xml.
            //
            // Este servlet serve imagens, incluindo imagens px
            // para espaçamento.
            //
            // Sirva as imagens diretamente para não
            // incorrermos em tempo extra associado a
            // a uma solicitação JSF para uma entidade não-JSF.
            exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, request.getContextPath() + "/image?image=");
        }else if("application/xlsx".equals(type)){
            exporter = new JRXlsxExporter();                
            exporter.setParameter(JRXlsExporterParameter.JASPER_PRINT, jasperPrint);                
            exporter.setParameter(JRXlsExporterParameter.OUTPUT_STREAM, response.getOutputStream());
            //exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_COLUMNS,new Boolean(true));                                
            exporter.setParameter(JRXlsExporterParameter.OUTPUT_FILE, name+".xlsx");
            exporter.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET, Boolean.FALSE);
            exporter.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);
            exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND, Boolean.FALSE);
            exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS, Boolean.TRUE);

        }else if("application/csv".equals(type)){
            exporter = new JRCsvExporter();             
            exporter.setParameter(JRCsvExporterParameter.JASPER_PRINT, jasperPrint);                
            exporter.setParameter(JRCsvExporterParameter.OUTPUT_STREAM, response.getOutputStream());
            exporter.setParameter(JRCsvExporterParameter.OUTPUT_FILE_NAME, name+".csv");
        }else if("application/docx".equals(type)){
            exporter = new JRDocxExporter();
            exporter.setParameter(JRDocxExporterParameter.JASPER_PRINT, jasperPrint);
            exporter.setParameter(JRDocxExporterParameter.OUTPUT_STREAM, response.getOutputStream());
        } else if("application/rtf".equals(type)){
            exporter = new JRRtfExporter();
            exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
            exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, response.getOutputStream());
        }
    } catch (RuntimeException e) {
        throw e;
    } catch (Exception e) {
        throw new FacesException(e);
    }

    try {
        exporter.exportReport();
    } catch (RuntimeException e) {
        throw e;
    } catch (Exception e) {
        throw new FacesException(e);
    }
    fcontext.responseComplete();
}

Summary 摘要

The "Content-Disposition" HTTP response header is not being set. 未设置"Content-Disposition" HTTP响应标头。 Set it using: 使用以下方法进行设置:

response.setHeader(
  "Content-Disposition",
  "attachment; filename=".concat(name).concat(filenameExtension)
);

But this is not the only problem. 但这不是唯一的问题。

Servlet vs. JSF Page Servlet与JSF页面

Although the question does not include how the report is being called, I'm going to assume it's one of: 尽管问题不包括如何调用报告,但我将假定它是以下之一:

<a href="report.jsf">Download</a>
<a href="report.xhtml">Download</a>

This will lead to troubles (such as exceptions due to the output stream being closed twice). 这将导致麻烦(例如由于两次关闭输出流而导致的异常)。 Use a Servlet, instead, to generate a report for download. 而是使用Servlet生成报告以供下载。 The link will become: 链接将变为:

<a href="/servlets/ReportServlet">Download</a>

See also: 也可以看看:

Don't use FacesContext to get the HTTP response stream. 不要使用FacesContext来获取HTTP响应流。 Use a Servlet, instead, and implement the doGet and doPost methods. 改用Servlet,并实现doGetdoPost方法。

Code Simplifications 代码简化

The following code: 如下代码:

boolean found = false;
for (int i = 0; i < VALID_TYPES.length; i++) {
    if (VALID_TYPES[i].equals(type)) {
        found = true;
        break;
    }
}
if (!found) {
    throw new IllegalArgumentException("Tipo solicitado '" + type + "' inválido");
}

Reduces to: 减少到:

if( !Arrays.asList(VALID_TYPES).contains(type) ) {
    throw new IllegalArgumentException("Tipo solicitado '" + type + "' inválido");
}

Create a ReportFormat enumeration that associates a file extension with its application type in a robust, reusable way: 创建一个ReportFormat枚举,以可靠,可重用的方式将文件扩展名与其应用程序类型相关联:

public enum ReportFormat {

    /**
     * Adobe Acrobat Portable Document Format.
     *
     * @see https://tools.ietf.org/html/rfc3778
     */
    PDF("application/pdf", "pdf"),

    /**
     * Hypertext Mark-up Language.
     *
     * @see https://www.ietf.org/rfc/rfc2854.txt
     */
    HTML("text/html", "html"),

    /**
     * Comma-separated Values.
     *
     * @see https://tools.ietf.org/html/rfc4180
     */
    CSV("text/csv", "csv"),

    /**
     * Proprietary Microsoft Excel Format (see also: CSV).
     *
     * @see http://www.iana.org/assignments/media-types/application/vnd.ms-excel
     */
    XLS("application/vnd.ms-excel", "xls"),

    /**
     * The media type as defined by IANA and IETF.
     *
     * @see http://www.iana.org/assignments/media-types/media-types.xhtml
     */
    private final String mediaType;

    /**
     * The filename extension typically used for this format's media type.
     */
    private final String extension;

    private ReportFormat(
            final String mediaType,
            final String extension) {
        this.mediaType = mediaType;
        this.extension = extension;
    }

    public String getFilenameExtension() {
        return this.extension;
    }

    /**
     * Returns the media type (formerly MIME type) for this report format
     * suitable for inclusion in the content-header of an HTTP response.
     *
     * @return The report format media type.
     * @see http://www.iana.org/assignments/media-types/media-types.xhtml
     */
    public String getMediaType() {
        return this.mediaType;
    }
}

Now, instead of passing in the type , you can write: 现在,无需传递type ,您可以编写:

public void gerarJasper(String name, ReportFormat reportFormat, ... ) {
}

Then there's no need to check for the report format because only known types can be passed. 然后,无需检查报告格式,因为只能传递已知类型。 This further reduces the code to: 这进一步将代码减少为:

if( reportFormat == null ) {
    throw new IllegalArgumentException("Tipo solicitado null inválido");
}

Alternatively, assume a default format and the method will throw one few error conditions to handle: 或者,假定使用默认格式,该方法将抛出一些错误条件来处理:

if( reportFormat == null ) {
    // Returns ReportFormat.PDF by default.
    reportFormat = getDefaultFormat();
}

Next, the following code: 接下来,下面的代码:

} catch (RuntimeException e) {
    throw e;
} catch (Exception e) {
    throw new FacesException(e);
}

Reduces to: 减少到:

} catch (Exception e) {
    throw new FacesException(e);
}

There are a number of other simplifications that can be made. 可以进行许多其他简化。 See the Command Pattern for details. 有关详细信息,请参见命令模式 For example: 例如:

FacesContext fc = FacesContext.getCurrentInstance();
FacesContext fcontext = FacesContext.getCurrentInstance();

Only one FacesContext instance is necessary, so you can delete fcontext (or fc ). 只需要一个FacesContext实例,因此您可以删除fcontext (或fc )。

As for the problem, the content disposition is not set via the HTTP response. 对于该问题,未通过HTTP响应设置内容处置。 With ReportFormat in place, create some new methods: 使用ReportFormat后,创建一些新方法:

private void setHeader(final String name, final String value) {
    getResponse().setHeader(name, value);
}

private HttpServletResponse getResponse() {
    final ExternalContext ctx = getFacesContext().getExternalContext();
    final HttpServletResponse response = (HttpServletResponse) ctx.getResponse();

    return response;
}

Next, introduce a constant and additional methods: 接下来,介绍一个常量和其他方法:

private static final String CONTENT_DISPOSITION = "Content-Disposition";

protected void setContentType(final String mediaType) {
    getResponse().setContentType(mediaType);
}

protected void setContentDisposition(final String filename) {
    setHeader(CONTENT_DISPOSITION, "attachment; filename=".concat(filename));
}

Call them like: 像这样称呼他们:

setContentType( reportFormat.getMediaType() );
setContentDisposition( name + "." + reportFormat.getFilenameExtension() );

The code shown in the question is overly complex. 问题中显示的代码过于复杂。 Applying some common design patterns will make it easier to maintain. 应用一些常见的设计模式将使其易于维护。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM