如何流式传输文件下载并显示一个JSF面孔消息? [英] How to stream a file download and display a JSF faces message?

查看:108
本文介绍了如何流式传输文件下载并显示一个JSF面孔消息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在向用户发送二进制文件,按照SO问题如何从JSF支持bean提供文件下载?



一般来说,工作流程按照预期方式工作,但是在生成导出文件可恢复时可能会发生错误,我们希望将这些显示为对用户的警告。在这种情况下文件本身仍然会生成。所以我们希望导出继续显示面孔消息。



只是为了强调这一点:是的,有一些不符合数据,但我们的用户希望导出继续并收到有缺陷的文件。然后他们想看看这个文件,联系他们的供应商,并给他发送一个关于这个缺陷的消息。



所以我需要导出完成任何情况。 / p>

但是,它并不适用于我们想要的。我已经创建了一个简化的例子来说明我们的方法。



作为替代方案,我们正在考虑一个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,谢谢帮助!getBytes();
//这里有一些不好的事情,用户应该知道
//但是这个消息不会出来给用户
fc.addMessage(null,new FacesMessage(record 2 has 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页面 p>

 <!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 =downloadvalue =下载
actionListener =#{exportController.export()}/>
< / h:form>
< / h:body>
< / f:view>
< / html>


解决方案

由于您实际执行文件下载响应不是一个JSF,在同一个请求发生时,不可能添加你的消息。对我来说,最干净的解决方案是避免使用异步请求,就是使用一个 @ViewScoped bean,然后分两步完成。所以,要有一个准备文件的按钮,稍后通知用户,并准备下载它:

  @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,谢谢帮助!getBytes();
//这里有一些不好的事情,用户应该知道
FacesContext.getCurrentInstance()。addMessage(null,
new FacesMessage(record 2 is 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 =downloadvalue =下载
disabled =#{not exportController.ready}
action =#{exportController.export()} />
< / h:form>
< / h:body>
< / f:view>
< / html>

请注意,此解决方案可能对小文件有效(他们的整个内容存储在内存中,而用户保持同样的观点)。但是,如果要使用大文件(或大量用户),最好将其内容存储在临时文件中,并显示一个链接,而不是下载按钮。



另请参见:




We are streaming a binary file to our users, following the procedure elaborated in the SO question How to provide a file download from a JSF backing bean?

In general the workflow works as intended, but during the generation of the export file recoverable errors may occur and we want to display these as a warning to the user. The file itself shall still be generated in that case. So we want that export to continue and display faces messages.

Just to put emphasis on this: Yes, there is something not OK with the data, but our users want the export to continue and receive that flawed file anyway. Then they want to have a look at the file, contact their vendor and send him a message about the flaw.

So I need the export to finish in any case.

But it does not work out as we want it to. I have created a simplified example to illustrate our approach.

As alternative we are considering a Bean that will be hold the messages and display them after the export. But probably there is a way with JSF built-in mechanisms to achieve this.

Controller

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 Page

<!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>

解决方案

Since you're actually performing a file download response and not a JSF one, it's not possible for your message to be added while the same request happens. The most clean solution for me, avoiding hacky asynchronous requests is to use a @ViewScoped bean and do your task in two steps. So, to have a button for preparing your file, notifying the user later on and allowing him to download it when it's ready:

@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>

Note this solution could be valid for small files (their entire content is stored in memory while user keeps in the same view). However, if you're going to use it with large files (or large number of users) your best is to store its content in a temporary file and display a link to it instead of a download button. That's what @BalusC suggests in the reference below.

See also:

这篇关于如何流式传输文件下载并显示一个JSF面孔消息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆