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

时间:2013-05-30 13:08:28

标签: jsf file-upload jsf-2.2

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

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

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

正如您所看到的,由于有两个Part具有相同的clientId,因此它始终使用最后一个而不是传递列表。

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

3 个答案:

答案 0 :(得分:12)

  

如何使用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" />

但是,<h:inputFile>组件本身不支持从请求中获取多个Part并将其设置为数组或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开始提供<o:inputFile>,这应该使多个和目录选择不那么繁琐。

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

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

该值可以绑定到List<Part>

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

答案 1 :(得分:1)

我认为可以使用标准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.File系列组件的javax.faces.Input类型的JSF渲染器类FileRenderer无法正确处理此情况。

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

我认为更好的策略是始终将组件的值设为List<Part>,而不仅仅是建议herePart并实施here

第3步:

使其工作的最后一件事是在faces-config.xml中配置这样修改过的多文件渲染器类,将以下内容添加到<faces-config>根元素:

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

答案 2 :(得分:0)

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