JSF文件下载异常处理(如何防止视图重绘)

JSF file download exception handling (how to prevent view rerendering)

朋友们,美好的一天!

首先,我为我的英语感到抱歉。这是我的问题:

我是 JSF (2.0) 的新手,我正在尝试使用 BalusC 算法从托管 bean 下载文件。功能正常运行,"Save As..." 对话框出现在浏览器中。但是我不知道如何在没有视图 reload/redirect.

的情况下 return 文件下载错误消息(支持 bean 方法中的异常、数据库错误等)

我在视图上的隐藏按钮:

    <h:form id="detailsDecisionMainForm">
        <h:inputHidden id="docId" value="" />
        <h:commandButton id="downloadAction" action="#{detailsDecisionGridBean.downloadForm()}" style="display: none;" />
    </h:form>

我的托管 bean(我可以使用哪个范围?我试过请求和查看范围)方法:

public String downloadForm() {
    log.fine("downloadForm call");
    PdfService pdfService = new PdfServiceImpl();
    ArmCommonService armCommonService = new ArmCommonServiceImpl();
    String errorMessage = null;
    try {
        Long opUni = new Long(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("detailsDecisionMainForm:docId"));
        log.fine("Document opUni = " + opUni);
        DocXML docXML = armCommonService.getDocXMLById(opUni);
        if (docXML != null) {
            File pdfFile = pdfService.getPdfReport(docXML.getXml(), docXML.getType());
            if (pdfFile != null) {
                DownloadUtils.exportPdf(pdfFile);
            } else {
                log.log(Level.SEVERE, "downloadForm error: pdf generation error");
                errorMessage = "PDF-document generation error.";
            }
        } else {
            log.log(Level.SEVERE, "downloadForm error: xml not found");
            errorMessage = "XML-document not found.";
        }
    } catch (Exception ex) {
        log.log(Level.SEVERE, "downloadForm exception: " + ex.getMessage());
        errorMessage = "File download exception.";
    }
    if (errorMessage != null) {
        FacesContext.getCurrentInstance().addMessage("detailsDecisionMainForm:downloadAction", new FacesMessage(errorMessage));
    }
    return null;
}

DownloadUtils.exportPdf() 程序正常工作:

public static void exportPdf(File file) throws IOException {
    InputStream fileIS = null;
    try {
        log.fine("exportPdf call");
        fileIS = new FileInputStream(file);
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();
        ec.responseReset();
        ec.setResponseContentType(APPLICATION_PDF_UTF_8);
        byte[] buffer = ByteStreams.toByteArray(fileIS);
        ec.setResponseContentLength(buffer.length);
        ec.setResponseHeader(HttpHeaders.CONTENT_DISPOSITION, String.format(CONTENT_DISPOSITION_VALUE, new String(file.getName().getBytes(StandardCharsets.UTF_8))));
        ec.getResponseOutputStream().write(buffer);
        fc.responseComplete();
    } catch (Exception ex) {
        log.log(Level.SEVERE, "exportPdf exception: " + ex.getMessage());
    } finally {
        if (fileIS != null) {
            fileIS.close();
            log.fine("exportPdf inputstream file closed");
        }
    }
}

如何防止在 downloadForm() 之后重新呈现视图 error/exception?以及如何显示带有消息文本的 javascript alert()(将来 - jQuery.messager 带有错误文本的面板)?

谢谢!

为了防止重新加载整个页面,您必须在 ajax 之前提交表单。但是,为了能够下载文件,您必须关闭 ajax。这并不能很好地结合在一起。

最好的办法是将操作分成两个请求。首先发送一个 ajax 请求,在服务器端的临时位置创建文件。当失败时,您可以按照通常的方式显示面孔消息。成功后,您可以通过条件呈现的 JavaScript 提交隐藏的非 ajax 命令按钮来自动触发第二个请求。然后,第二个请求可以将已经成功创建的文件从临时位置流式传输到响应。

之前已经提出并回答了类似的问题:. But this involves PrimeFaces and OmniFaces。以下是标准的 JSF 方法:

<h:form id="form">
    ...
    <h:commandButton value="Export" action="#{bean.export}">
        <f:ajax ... render="result" />
    </h:commandButton>
    <h:panelGroup id="result">
        <h:messages />
        <h:commandButton id="download" action="#{bean.download}"
            style="display:none" />
        <h:outputScript rendered="#{not facesContext.validationFailed}">
            document.getElementById("form:download").onclick();
        </h:outputScript>
    </h:panelGroup>
</h:form>

并使用此 @ViewScoped bean(逻辑基于您现有的逻辑)。基本上,只需在导出操作 (ajax) 期间获取 File 作为实例变量,然后在下载操作(非 ajax)期间将其流式传输。

private File pdfFile;

public void export() {
    try {
        pdfFile = pdfService.getPdfReport(...);
    } catch (Exception e) {
        context.addMessage(...);
    }
}

public void download() throws IOException {
    DownloadUtils.exportPdf(pdfFile);
}

另请参阅:

  • How to invoke a JSF managed bean on a HTML DOM event using native JavaScript?
  • Store PDF for a limited time on app server and make it available for download