簡體   English   中英

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

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

Web應用程序可以毫無問題地打印PDF報告,但是如果xlsx,docx,csv,rtf等報告均未正確配置。 瀏覽器將嘗試始終以.xhtml擴展名保存文件。

您如何將報告導出到瀏覽器,以便使用正確的文件名和媒體類型導出文件?

碼:

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();
}

摘要

未設置"Content-Disposition" HTTP響應標頭。 使用以下方法進行設置:

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

但這不是唯一的問題。

Servlet與JSF頁面

盡管問題不包括如何調用報告,但我將假定它是以下之一:

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

這將導致麻煩(例如由於兩次關閉輸出流而導致的異常)。 而是使用Servlet生成報告以供下載。 鏈接將變為:

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

也可以看看:

不要使用FacesContext來獲取HTTP響應流。 改用Servlet,並實現doGetdoPost方法。

代碼簡化

如下代碼:

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");
}

減少到:

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

創建一個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;
    }
}

現在,無需傳遞type ,您可以編寫:

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

然后,無需檢查報告格式,因為只能傳遞已知類型。 這進一步將代碼減少為:

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

或者,假定使用默認格式,該方法將拋出一些錯誤條件來處理:

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

接下來,下面的代碼:

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

減少到:

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

可以進行許多其他簡化。 有關詳細信息,請參見命令模式 例如:

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

只需要一個FacesContext實例,因此您可以刪除fcontext (或fc )。

對於該問題,未通過HTTP響應設置內容處置。 使用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;
}

接下來,介紹一個常量和其他方法:

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));
}

像這樣稱呼他們:

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

問題中顯示的代碼過於復雜。 應用一些常見的設計模式將使其易於維護。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM