14

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

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

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

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

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

4

3 回答 3

14

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

您确实可以使用另一个 JSF 2.2 特性来实现这一点:传递属性。将该multiple属性设置为直通属性(浏览器支持目前相当广泛)。

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

但是,<h:inputFile>组件本身不支持Part从请求中抓取多个 s 并将其设置为数组或Collectionbean 属性。它只会设置与输入字段名称匹配的最后一部分。基本上,为了支持多个部分,需要创建一个自定义渲染器(并且您应该立即抓住机会立即支持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;
}

请注意,为了安全和清晰起见,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" />

该值可以绑定到 a List<Part>

private List<Part> files; // +getter+setter
于 2016-04-08T15:12:42.320 回答
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步:

组件族类型的JSF 渲染器类FileRenderer不能正确处理这种情况。javax.faces.Filejavax.faces.Input

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

我认为更好的策略是始终让组件的值成为 aList<Part>而不是这里Part建议并此处实现。

第 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>
于 2013-09-18T05:54:27.867 回答
0

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

于 2013-08-16T09:22:46.327 回答