【问题标题】:How to stream a file download and display a JSF faces message?如何流式传输文件下载并显示 JSF 面孔消息?
【发布时间】:2014-10-30 12:14:55
【问题描述】:

我们正在按照 SO 问题How to provide a file download from a JSF backing bean? 中详细说明的过程向我们的用户流式传输二进制文件

一般来说,工作流程按预期工作,但在生成导出文件的过程中可能会出现可恢复错误,我们希望将这些错误显示为向用户发出的警告。在这种情况下仍应生成文件本身。因此,我们希望该导出继续显示人脸消息。

只是强调这一点:是的,数据有问题,但我们的用户希望继续导出并接收有缺陷的文件。然后他们想查看文件,联系他们的供应商并向他发送有关该缺陷的消息。

所以无论如何我都需要完成导出。

但它并没有如我们所愿。我创建了一个简化示例来说明我们的方法。

作为替代方案,我们正在考虑使用 Bean 来保存消息并在导出后显示它们。但是可能有一种方法可以使用 JSF 内置机制来实现这一点。

控制器

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import org.apache.tomcat.util.http.fileupload.util.Streams;

@ManagedBean
@RequestScoped
public class ExportController {

    public void export() {
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();

        byte[] exportContent = "Hy Buddys, thanks for the help!".getBytes();
        // here something bad happens that the user should know about
        // but this message does not go out to the user
        fc.addMessage(null, new FacesMessage("record 2 was flawed"));

        ec.responseReset();
        ec.setResponseContentType("text/plain");
        ec.setResponseContentLength(exportContent.length);
        String attachmentName = "attachment; filename=\"export.txt\"";
        ec.setResponseHeader("Content-Disposition", attachmentName);
        try {
            OutputStream output = ec.getResponseOutputStream();
            Streams.copy(new ByteArrayInputStream(exportContent), output, false);
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        fc.responseComplete();
    }
}

JSF 页面

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:p="http://primefaces.org/ui">

    <f:view contentType="text/html">
        <h:body>
            <h:form prependId="false">
                <h:messages id="messages" />
                <h:commandButton id="download" value="Download"
                                 actionListener="#{exportController.export()}" />
            </h:form>
        </h:body>
    </f:view>
</html>

【问题讨论】:

  • +1 获取正确的 SSCCE 示例。
  • org.apache.tomcat.util.http.fileupload.util.Streams 是 Tomcat 特定的。

标签: jsf jsf-2 download message


【解决方案1】:

你可以试试这个: 对于 primefaces,您可以使用远程命令而不是命令链接,并在成功时使用其名称调用它。否则,给 commandlink 一个小部件 var 并调用它的 click 方法。

【讨论】:

  • 添加更多描述和有用的内容来支持您的回答会很有帮助。否则这只会被删除。
【解决方案2】:

由于您实际上是在执行文件下载响应而不是 JSF 响应,因此在发生相同请求时无法添加您的消息。对我来说,最干净的解决方案是使用 @ViewScoped bean 并分两步完成您的任务,避免 hacky 异步请求。因此,要有一个用于准备文件的按钮,稍后通知用户并允许他在准备好时下载它:

@ManagedBean
@ViewScoped
public class ExportController implements Serializable {

    private byte[] exportContent;

    public boolean isReady() {
        return exportContent != null;
    }

    public void export() {
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();
        ec.responseReset();
        ec.setResponseContentType("text/plain");
        ec.setResponseContentLength(exportContent.length);
        String attachmentName = "attachment; filename=\"export.txt\"";
        ec.setResponseHeader("Content-Disposition", attachmentName);
        try {
            OutputStream output = ec.getResponseOutputStream();
            Streams.copy(new ByteArrayInputStream(exportContent), output, false);
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        fc.responseComplete();
    }

    public void prepareFile() {
        exportContent = "Hy Buddys, thanks for the help!".getBytes();
        // here something bad happens that the user should know about
        FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage("record 2 was flawed"));
    }
}
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:p="http://primefaces.org/ui">

<f:view contentType="text/html">
    <h:body>
        <h:form>
            <h:messages id="messages" />
            <h:commandButton value="Prepare"
                action="#{exportController.prepareFile}" />
            <h:commandButton id="download" value="Download"
                disabled="#{not exportController.ready}"
                action="#{exportController.export()}" />
        </h:form>
    </h:body>
</f:view>
</html>

请注意,此解决方案可能对小文件有效(它们的全部内容存储在内存中,而用户保持在同一视图中)。但是,如果您要将它用于大文件(或大量用户),最好将其内容存储在临时文件中并显示指向它的链接而不是下载按钮。这就是@BalusC 在下面的参考资料中所建议的。

另请参阅:

【讨论】:

  • 你可以使用JS来调用下一个动作。
  • @BalusC 很高兴看到您的 JS 解决方案。能发个链接吗?
猜你喜欢
  • 2014-12-12
  • 1970-01-01
  • 2015-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-04
  • 2011-08-07
  • 1970-01-01
相关资源
最近更新 更多