如何使用 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 并将其设置为数组或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;
}
请注意,为了安全和清晰起见,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