如何从JSF备份bean提供文件下载? [英] How to provide a file download from a JSF backing bean?

查看:160
本文介绍了如何从JSF备份bean提供文件下载?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有办法从JSF备份bean动作方法提供文件下载?
我尝试了很多东西。主要的问题是,我无法确定如何获取响应的 OutputStream ,以便将文件内容写入。我知道如何使用 Servlet ,但是这不能从JSF表单中调用,并且需要一个新的请求。



如何获取当前 FacesContext 的响应的 OutputStream

解决方案

简介



您可以通过 的ExternalContext 。在JSF 1.x中,您可以通过 HttpServletResponse 对象面/上下文/ ExternalContext.html#GETRESPONSE%28%29\" > 的ExternalContext#GETRESPONSE() 。在JSF 2.x中,您可以使用一些新的委托方法,如 ExternalContext#getResponseOutputStream() ,无需从JSF下抓取 HttpServletResponse 抽油烟机。



在响应中,您应该设置 Content-Type 头,以便客户端知道要关联的应用程序提供的文件。而且,您应该设置 Content-Length 头,以便客户端可以计算下载进度,否则将是未知的。而且,如果您想要另存为,您应该将 Content-Disposition 标题设置为附件 >对话框,否则客户端将尝试内联显示。最后只需将文件内容写入响应输出流。



最重要的部分是调用 FacesContext#responseComplete() 通知JSF不应该在将文件写入响应后执行导航和渲染,否则响应结束将被页面的HTML内容污染,或者在较旧的JSF版本中,您将获得 IllegalStateException ,当JSF实现调用 getWriter() getoutputstream() c $ c>呈现HTML。



通用JSF 2.x示例



  public void download()throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();

ec.responseReset(); //某些JSF组件库或某些Filter可能已预先在缓冲区中设置了一些头文件。我们想摆脱它们,否则可能会碰撞。
ec.setResponseContentType(contentType); //查看所有类型的http://www.iana.org/assignments/media-types。如果需要,请使用ExternalContext#getMimeType()根据文件名自动检测。
ec.setResponseContentLength(contentLength); //设置文件大小。此标题是可选的。它会工作,如果它被省略,但下载进度将是未知的。
ec.setResponseHeader(Content-Disposition,attachment; filename = \+ fileName +\); //另存为弹出魔法在这里完成。您可以给它任何您想要的文件名,这仅在MSIE中不起作用,它将使用当前请求URL作为文件名。

OutputStream output = ec.getResponseOutputStream();
//现在您可以通过常规方式将文件的InputStream写入上述OutputStream。
// ...

fc.responseComplete(); //重要!否则JSF将尝试渲染显然会失败的响应,因为它已经写入文件并关闭了。
}



通用JSF 1.x示例



  public void download()throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
HttpServletResponse response =(HttpServletResponse)fc.getExternalContext()。getResponse();

response.reset(); //某些JSF组件库或某些Filter可能已预先在缓冲区中设置了一些头文件。我们想摆脱它们,否则可能会碰撞。
response.setContentType(contentType); //查看所有类型的http://www.iana.org/assignments/media-types。如果需要使用ServletContext#getMimeType()根据文件名自动检测。
response.setContentLength(contentLength); //设置文件大小。此标题是可选的。它会工作,如果它被省略,但下载进度将是未知的。
response.setHeader(Content-Disposition,attachment; filename = \+ fileName +\); //另存为弹出魔法在这里完成。您可以给它任何您想要的文件名,这仅在MSIE中不起作用,它将使用当前请求URL作为文件名。

OutputStream output = response.getOutputStream();
//现在您可以通过常规方式将文件的InputStream写入上述OutputStream。
// ...

fc.responseComplete(); //重要!否则JSF将尝试渲染显然会失败的响应,因为它已经写入文件并关闭了。
}



常见的本地文件示例



如果您需要从本地磁盘文件系统中传输文件,请替换以下代码:

 文件文件= new File(/ path / to / file.ext); 
String fileName = file.getName();
String contentType = ec.getMimeType(fileName); // JSF 1.x:((ServletContext)ec.getContext())。getMimeType(fileName);
int contentLength =(int)file.length();

// ...

Files.copy(file.toPath(),output);



关闭ajax!



需要确保ajax请求调用的action方法不是,但是当您使用< h:commandLink> < h:commandButton> 。 Ajax请求由JavaScript处理,由于安全性原因,由于安全原因,没有任何设施强制使用ajax响应的内容进行另存为对话。



如果你使用例如PrimeFaces < p:commandXxx> ,那么您需要确保通过 ajax =false显式关闭ajax 属性。如果您使用的是ICEfaces,那么您需要在命令组件中嵌套一个< f:ajax disabled =true/>



实用程序



如果您使用JSF实用程序库 OmniFaces ,那么你可以使用三个方便的一个 Faces#sendFile() 方法采用文件 InputStream byte [] ,并指定该文件是否应作为附件下载( )或inline( false )。

  public void download()throws IOException {
Faces.sendFile(file,true);
}

是的,这段代码是按原样完成的。你不需要调用 responseComplete()等等。此方法也适用于特定于IE的头文件和UTF-8文件名。您可以在这里找到源代码。


Is there any way of providing a file download from a JSF backing bean action method? I have tried a lot of things. Main problem is that I cannot figure how to get the OutputStream of the response in order to write the file content to. I know how to do it with a Servlet, but this cannot be invoked from a JSF form and requires a new request.

How can I get the OutputStream of the response from the current FacesContext?

解决方案

Introduction

You can get everything through ExternalContext. In JSF 1.x, you can get the raw HttpServletResponse object by ExternalContext#getResponse(). In JSF 2.x, you can use the bunch of new delegate methods like ExternalContext#getResponseOutputStream() without the need to grab the HttpServletResponse from under the JSF hoods.

On the response, you should set the Content-Type header so that the client knows which application to associate with the provided file. And, you should set the Content-Length header so that the client can calculate the download progress, otherwise it will be unknown. And, you should set the Content-Disposition header to attachment if you want a Save As dialog, otherwise the client will attempt to display it inline. Finally just write the file content to the response output stream.

Most important part is to call FacesContext#responseComplete() to inform JSF that it should not perform navigation and rendering after you've written the file to the response, otherwise the end of the response will be polluted with the HTML content of the page, or in older JSF versions, you will get an IllegalStateException with a message like getoutputstream() has already been called for this response when the JSF implementation calls getWriter() to render HTML.

Generic JSF 2.x example

public void download() throws IOException {
    FacesContext fc = FacesContext.getCurrentInstance();
    ExternalContext ec = fc.getExternalContext();

    ec.responseReset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
    ec.setResponseContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ExternalContext#getMimeType() for auto-detection based on filename.
    ec.setResponseContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
    ec.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.

    OutputStream output = ec.getResponseOutputStream();
    // Now you can write the InputStream of the file to the above OutputStream the usual way.
    // ...

    fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}

Generic JSF 1.x example

public void download() throws IOException {
    FacesContext fc = FacesContext.getCurrentInstance();
    HttpServletResponse response = (HttpServletResponse) fc.getExternalContext().getResponse();

    response.reset(); // Some JSF component library or some Filter might have set some headers in the buffer beforehand. We want to get rid of them, else it may collide.
    response.setContentType(contentType); // Check http://www.iana.org/assignments/media-types for all types. Use if necessary ServletContext#getMimeType() for auto-detection based on filename.
    response.setContentLength(contentLength); // Set it with the file size. This header is optional. It will work if it's omitted, but the download progress will be unknown.
    response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); // The Save As popup magic is done here. You can give it any file name you want, this only won't work in MSIE, it will use current request URL as file name instead.

    OutputStream output = response.getOutputStream();
    // Now you can write the InputStream of the file to the above OutputStream the usual way.
    // ...

    fc.responseComplete(); // Important! Otherwise JSF will attempt to render the response which obviously will fail since it's already written with a file and closed.
}

Common local file example

In case you need to stream a file from the local disk file system, substitute the code as below:

File file = new File("/path/to/file.ext");
String fileName = file.getName();
String contentType = ec.getMimeType(fileName); // JSF 1.x: ((ServletContext) ec.getContext()).getMimeType(fileName);
int contentLength = (int) file.length();

// ...

Files.copy(file.toPath(), output);

Turn off ajax!

You only need to make sure that the action method is not called by an ajax request, but that it is called by a normal request as you fire with <h:commandLink> and <h:commandButton>. Ajax requests are handled by JavaScript which in turn has, due to security reasons, no facilities to force a Save As dialogue with the content of the ajax response.

In case you're using e.g. PrimeFaces <p:commandXxx>, then you need to make sure that you explicitly turn off ajax via ajax="false" attribute. In case you're using ICEfaces, then you need to nest a <f:ajax disabled="true" /> in the command component.

Utility method

If you're using JSF utility library OmniFaces, then you can use one of the three convenient Faces#sendFile() methods taking either a File, or an InputStream, or a byte[], and specifying whether the file should be downloaded as an attachment (true) or inline (false).

public void download() throws IOException {
    Faces.sendFile(file, true);
}

Yes, this code is complete as-is. You don't need to invoke responseComplete() and so on yourself. This method also properly deals with IE-specific headers and UTF-8 filenames. You can find source code here.

这篇关于如何从JSF备份bean提供文件下载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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