【问题标题】:How can I upload multiple files with JSF 2.2如何使用 JSF 2.2 上传多个文件
【发布时间】:2013-05-26 01:59:27
【问题描述】:

我正在尝试使用h:inputFile 添加多文件上传。我快速浏览了源代码,它似乎没有呈现multiple="multiple" 的选项。有没有办法在不编写自定义组件的情况下解决这个问题? 如果没有,是否有建议的自定义 JSF2.2 组件可以处理多个 Ajax 文件上传?

更新: 我已经使用passthrough 标签传递了multiple="multiple",但是当我调试FileRenderer 时,相关的代码会用第二个文件覆盖第一个文件:

for (Part cur : parts) {
  if (clientId.equals(cur.getName())) {
    component.setTransient(true);
    setSubmittedValue(component, cur);
  }
}

如你所见,由于有两个Parts 具有相同的clientId,它总是使用最后一个而不是传递一个列表。

如果有,请推荐一个替代方案。

【问题讨论】:

  • 在我看来,multiple 属性和 JSF 实现并没有符合规范 spec,这对我来说似乎很奇怪。由于我找不到省略它的合乎逻辑的理由,有谁知道为什么?现在,我将使用现有的 JS/JQuery fileUpload 库之一来实现一个自定义组件,或者使用像 tomahawk 或 prime/richfaces 库这样的 JSF 文件上传。

标签: jsf file-upload jsf-2.2


【解决方案1】:

即使是很久以前的事了:考虑到您自己的评论,我会推荐一个像 PrimeFaces fileUploadMultiple 这样的组件,提到不要忘记 web.xml 中所需的更改以及上传所需的所有库。根据您的需要,将其视为一种解决方法或完整的解决方案。 PrimeFaces 是一个相当不错的组件库

【讨论】:

  • 感谢您的回复。我正在使用 jsf-2.2,它已经有很多问题,所以我不愿意使用 3rd 方工具。我已经扩展了 JSF 以支持多次上传并且工作得很好。
【解决方案2】:

我认为可以使用标准 JSF 2.2 使用 passthrough 标签来上传多个文件。

第 1 步:

<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://xmlns.jcp.org/jsf/html"
  xmlns:pt="http://xmlns.jcp.org/jsf/passthrough">
...

<h:form id="form" enctype="multipart/form-data">
    <h:inputFile id="file" value="#{fileUploadBean.uploadedFile}" pt:multiple="multiple" />
    ...

第 2 步:

javax.faces.Input 系列组件的 javax.faces.File 类型的 JSF 渲染器类 FileRenderer 无法正确处理这种情况。

相反,当它遍历表单的各个部分时,它只是用上传集合中的每个文件覆盖前面的部分。

我认为更好的策略是始终将组件的值设为List&lt;Part&gt;,而不是像建议的here 和实施的here 那样只是Part

第三步:

最后一件事是在faces-config.xml 中配置这种修改后的多文件渲染器类,将以下内容添加到&lt;faces-config&gt; 根元素中:

<render-kit>
    <renderer>
        <description>Multiple File Renderer</description>
        <component-family>javax.faces.Input</component-family>
        <renderer-type>javax.faces.File</renderer-type>
        <renderer-class>com.example.MultipleFileRenderer</renderer-class>
    </renderer>
</render-kit>

【讨论】:

    【解决方案3】:

    如何使用 JSF 2.2 上传多个文件

    您确实可以通过另一个 JSF 2.2 功能实现这一点:passthrough attributes。将multiple 属性设置为直通属性 (browser support is currently quite broad)。

    <html ... xmlns:a="http://xmlns.jcp.org/jsf/passthrough">
    ...
    <h:inputFile ... a:multiple="true" />
    

    但是,&lt;h:inputFile&gt; 组件本身不支持从请求中获取多个Parts 并将其设置为数组或Collection bean 属性。它只会设置与输入字段名称匹配的最后一部分。基本上,为了支持多个部分,需要创建一个自定义渲染器(您应该立即抓住机会立即支持multiple 属性,而无需求助于传递属性)。

    为了在不创建整个渲染器的情况下获得“解决方法”,您可以借助以下小实用方法通过HttpServletRequest 手动获取所有部分:

    public static Collection<Part> getAllParts(Part part) throws ServletException, IOException {
        HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
        return request.getParts().stream().filter(p -> part.getName().equals(p.getName())).collect(Collectors.toList());
    }
    

    因此,下面的构造应该与上述实用方法一起使用:

    <h:inputFile value="#{bean.part}" a:multiple="true" />
    <h:commandButton ... action="#{bean.submit}" />
    

    private Part file;
    
    public void submit() throws ServletException, IOException {
        for (Part part : getAllParts(file)) {
            String fileName = part.getSubmittedFileName();
            InputStream fileContent = part.getInputStream();
            // ... 
            // Do your thing with it.
            // E.g. https://stackoverflow.com/q/14211843/157882
        }
    }
    
    public Part getFile() {
        return null; // Important!
    }
    
    public void setFile(Part file) {
        this.file = file;
    }
    

    请注意,safety and clarity 的 getter 最好总是返回 null。其实整个 getter 方法应该是不必要的,但它就是这样。

    在更现代的浏览器上,您甚至可以选择整个文件夹。这只需要一个更新的directory 属性。从 Firefox 46 开始支持此功能(从 42 开始,但需要在 about:config 中显式启用)。基于 Webkit 的浏览器(Chrome 11+、Safari 4+ 和 Edge)通过专有的 webkitdirectory 属性支持此功能。因此,如果您同时指定这两个属性,则通常是安全的。

    <h:inputFile ... a:multiple="true" a:directory="true" a:webkitdirectory="true" />
    

    请注意,这不会发送物理文件夹,而只会发送这些文件夹中包含的文件。


    更新:如果您碰巧使用 JSF 实用程序库 OmniFaces,从 2.5 版开始,提供了 &lt;o:inputFile&gt;,这应该可以减少多个和目录选择的繁琐。

    <o:inputFile value="#{bean.files}" multiple="true" />
    

    <o:inputFile value="#{bean.files}" directory="true" />
    

    该值可以绑定到List&lt;Part&gt;

    private List<Part> files; // +getter+setter
    

    【讨论】:

    猜你喜欢
    • 2013-09-12
    • 2020-12-07
    • 1970-01-01
    • 2016-08-13
    • 2015-02-05
    • 2012-03-19
    • 1970-01-01
    相关资源
    最近更新 更多