当前位置: 首页 > 知识库问答 >
问题:

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

岳阳飙
2023-03-14

有什么方法可以提供从JSF支持bean操作方法下载的文件吗?我试过很多东西。主要的问题是,我不知道如何获取响应的outputstream,以便将文件内容写入其中。我知道如何使用servlet来实现这一点,但是这不能从JSF表单调用,需要一个新的请求。

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

共有1个答案

周伟泽
2023-03-14

您可以通过ExternalContext获得所有内容。在JSF1.x中,可以通过ExternalContext#getResponse()获取原始HttpServletResponse对象。在JSF2.x中,您可以使用一系列新的委托方法,如ExternalContext#GetResponseOutputStream(),而不需要从JSF头罩下获取HttpServletResponse

在响应中,您应该设置content-type标头,以便客户机知道哪个应用程序与提供的文件关联。并且,您应该设置content-lengte标头,以便客户端能够计算下载进度,否则将是未知的。如果需要另存为对话框,则应将Content-Disposition标头设置为Attachment,否则客户端将尝试内联显示它。最后,只需将文件内容写入响应输出流。

最重要的部分是调用FacesContext#ResponseComplete(),以通知JSF在将文件写入响应后不应执行导航和呈现,否则响应的末尾将被页面的HTML内容污染,或者在旧的JSF版本中,当JSF实现调用GetWriter()呈现HTML时,您将得到一个IllegalStateException,其中包含一条消息,类似于已经为该响应调用了GetOutputStream()。

您只需要确保action方法不是由ajax请求调用的,而是由使用 激发的普通请求调用的。Ajax请求和远程命令由JavaScript处理,由于安全原因,JavaScript没有强制与Ajax响应内容进行另存为对话的工具。

如果您使用的是PrimeFaces ,那么您需要确保通过ajax=“false”属性显式关闭ajax。如果您使用的是ICEfaces,那么您需要在命令组件中嵌套一个

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.
}
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.
}

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

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);

如果您需要对动态生成的文件(如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();

例如。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();
public void download() throws IOException {
    Faces.sendFile(file, true);
}

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

 类似资料:
  • null null null 道: 处理与业务逻辑层的所有交互的非bean对象。它加载数据bean并准备提交,等等。我通常把它作为一类公共静态方法。 转换器、验证器: null 这似乎是一般JSF应用程序所需的全部内容。我已经阅读了以下内容:http://java.dzone.com/articles/making-distinctions-between,以及这里的回复:JSF backing

  • 我的响应一直在行抛出。bean属于请求范围。关于空指针有什么想法吗?

  • 问题内容: 我在隐藏的文本区域中有一些文本。单击按钮后,我希望提供文本作为文件下载。是否可以使用AngularJS或Javascript? 问题答案: 您可以使用进行类似的操作。 在您的控制器中: 为了启用URL: 请注意 每次调用createObjectURL()时,都会创建一个新的对象URL,即使您已经为同一对象创建了一个URL。当不再需要它们时,必须通过调用URL.revokeObjectU

  • 问题内容: 我正在使用selenium脚本,在其中尝试下载Excel文件并为其指定特定名称。这是我的代码: 无论如何,我可以给下载的文件指定一个特定的名称吗? 码: 问题答案: 您不能通过硒指定下载文件的名称。但是,您可以下载文件,在下载的文件夹中找到最新文件,然后根据需要重命名。 注意:从Google搜索中借用的方法可能有错误。但是你明白了。

  • 问题内容: 我希望站点上的用户能够下载路径被遮盖的文件,以便不能直接下载它们。 例如,我希望URL如下所示: http://example.com/download/?f=somefile.txt 在服务器上,我知道所有可下载文件都位于文件夹中。 有没有一种方法可以使Django提供该文件供下载,而不是尝试查找URL和查看以显示它? 问题答案: 对于“两全其美”,你可以将S.Lott的解决方案与x

  • 问题 如何用内置的cheerypy提供SSL支持? 解法 import web from web.wsgiserver import CherryPyWSGIServer CherryPyWSGIServer.ssl_certificate = "path/to/ssl_certificate" CherryPyWSGIServer.ssl_private_key = "path/to/ssl