如何从JSF支持bean提供文件下载?

zme*_*eda 85 jsf download jsf-2 backing-beans

有没有办法从JSF支持bean动作方法提供文件下载?我尝试过很多东西.主要问题是我无法弄清楚如何获取OutputStream响应以便将文件内容写入.我知道如何使用a Servlet,但这不能从JSF表单调用,需要新的请求.

如何OutputStream从当前获得响应FacesContext

Bal*_*usC 222

介绍

你可以通过一切ExternalContext.在JSF 1.x中,您可以获取原始HttpServletResponse对象ExternalContext#getResponse().在JSF 2.x中,您可以使用一堆新的委托方法,ExternalContext#getResponseOutputStream()而无需HttpServletResponse从JSF引擎盖下获取.

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

最重要的部分是调用FacesContext#responseComplete()告知JSF,在将文件写入响应后不应执行导航和呈现,否则响应的结尾将被页面的HTML内容或较旧的JSF版本污染,您将收到IllegalStateException一条消息,例如getoutputstream() has already been called for this responseJSF实现调用getWriter()呈现HTML 时的消息.

关闭ajax /不要使用远程命令!

您只需要确保ajax请求调用action方法,但是当你用<h:commandLink>和触发时,它会被正常请求调用<h:commandButton>.Ajax请求和远程命令由JavaScript处理,由于安全原因,它不会强制使用ajax响应的内容强制另存为对话.

如果您正在使用例如PrimeFaces <p:commandXxx>,那么您需要确保通过ajax="false"属性明确关闭ajax .如果您正在使用ICEfaces,则需要<f:ajax disabled="true" />在命令组件中嵌套a .

通用JSF 2.x示例

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.
}
Run Code Online (Sandbox Code Playgroud)

通用JSF 1.x示例

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.
}
Run Code Online (Sandbox Code Playgroud)

常见的静态文件示例

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

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);
Run Code Online (Sandbox Code Playgroud)

常见的动态文件示例

如果您需要流式传输动态生成的文件(如PDF或XLS),则只需提供output所使用的API所需的文件OutputStream.

例如iText PDF:

String fileName = "dynamic.pdf";
String contentType = "application/pdf";

// ...

Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, output);
document.open();
// Build PDF content here.
document.close();
Run Code Online (Sandbox Code Playgroud)

例如Apache POI HSSF:

String fileName = "dynamic.xls";
String contentType = "application/vnd.ms-excel";

// ...

HSSFWorkbook workbook = new HSSFWorkbook();
// Build XLS content here.
workbook.write(output);
workbook.close();
Run Code Online (Sandbox Code Playgroud)

请注意,您无法在此处设置内容长度.因此,您需要删除该行以设置响应内容长度.这在技术上没有问题,唯一的缺点是最终用户将呈现未知的下载进度.如果这很重要,那么你真的需要先写一个本地(临时)文件,然后按照前一章所示提供它.

实用方法

如果您正在使用JSF实用程序库OmniFaces,那么您可以使用三种方便的Faces#sendFile()方法之一来获取a File,InputStreama或a byte[],并指定该文件是作为attachment(true)还是inline(false)下载.

public void download() throws IOException {
    Faces.sendFile(file, true);
}
Run Code Online (Sandbox Code Playgroud)

是的,此代码按原样完成.你不需要自己调用responseComplete()等等.此方法还可以正确处理特定于IE的标头和UTF-8文件名.你可以在这里找到源代码.

  • @BalusC 你涵盖了每个 jsf 主题 - 谢谢你让我的生活更轻松,先生! (2认同)

Joh*_*des 5

public void download() throws IOException
{

    File file = new File("file.txt");

    FacesContext facesContext = FacesContext.getCurrentInstance();

    HttpServletResponse response = 
            (HttpServletResponse) facesContext.getExternalContext().getResponse();

    response.reset();
    response.setHeader("Content-Type", "application/octet-stream");
    response.setHeader("Content-Disposition", "attachment;filename=file.txt");

    OutputStream responseOutputStream = response.getOutputStream();

    InputStream fileInputStream = new FileInputStream(file);

    byte[] bytesBuffer = new byte[2048];
    int bytesRead;
    while ((bytesRead = fileInputStream.read(bytesBuffer)) > 0) 
    {
        responseOutputStream.write(bytesBuffer, 0, bytesRead);
    }

    responseOutputStream.flush();

    fileInputStream.close();
    responseOutputStream.close();

    facesContext.responseComplete();

}
Run Code Online (Sandbox Code Playgroud)