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 响应 header 。设置它使用:
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>
另请参阅:
- https://github.com/deadlydirk/jasperreports-example/blob/master/src/main/java/be/example/jasper/servlet/ReportServlet.java
- https://stackoverflow.com/a/35698286/59087
不要使用FacesContext
来获取HTTP响应流。请改用 Servlet,并实现 doGet
和 doPost
方法。
代码简化
以下代码:
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;
}
}
现在,您可以编写:
,而不是传入类型
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);
}
还可以进行许多其他简化。请参阅Command Pattern了解详情。例如:
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() );
问题中显示的代码过于复杂。应用一些常见的设计模式将使其更易于维护。
关于java - Jasper Reports 以 xlsx、docx、csv、rtf 等格式打印未配置的报告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48309313/