[英]How can I upload multiple files with JSF 2.2
我正在尝试使用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);
}
}
如您所见,由于有两个Part
具有相同的clientId
,它总是使用最后一个而不是传递一个列表。
如果有,请推荐一个替代方案。
如何使用JSF 2.2上传多个文件
您确实可以使用另一个JSF 2.2功能实现此功能: 直通属性 。 将multiple
属性设置为passthrough属性( 浏览器支持目前相当广泛 )。
<html ... xmlns:a="http://xmlns.jcp.org/jsf/passthrough">
...
<h:inputFile ... a:multiple="true" />
但是, <h:inputFile>
组件本身不支持从请求中获取多个Part
并将其设置为数组或Collection
bean属性。 它只会设置与输入字段名称匹配的最后一部分。 基本上,为了支持多个部分,需要创建一个自定义渲染器(您应该立即利用这个机会立即支持multiple
属性而不依赖于passthrough属性)。
为了在不创建整个渲染器的情况下使用“变通方法”,您可以通过以下小实用程序方法通过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
属性支持此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
我认为使用passthrough标签使用标准JSF 2.2可以使用多个文件上传。
<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" />
...
javax.faces.Input
系列组件的javax.faces.File
类型的JSF渲染器类FileRenderer无法正确处理此情况。
相反,当它遍历表单的各个部分时,它只是用上传集合中的每个文件覆盖前一部分。
我认为,一个更好的策略是总是有分量的值是一个List<Part>
,而不仅仅是Part
的建议在这里和实施在这里 。
使其工作的最后一件事是在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>
即使是很久以前:考虑到你自己的评论,我会推荐像PrimeFaces fileUploadMultiple这样的组件,提到不要忘记web.xml
所需的更改以及所有需要上传的库。 根据您的需求,将其视为变通方法或完整解决方案。 PrimeFaces是一个相当不错的组件库
由于这个问题是很久以前提出的,我想在这里提供更新。 如果您正在使用新的Jakarta EE Faces 4.0规范,那么支持多个文件上传将变得非常简单:
如前所述, h:from
必须使用 enctype "multipart/form-data"
进行扩展。 而h:inputFile
需要 passthrough 属性multiple=true
:
<ui:composition template="/WEB-INF/templates/layout.xhtml"
xmlns:faces="jakarta.faces" xmlns:f="jakarta.faces.core"
xmlns:h="jakarta.faces.html" xmlns:ui="jakarta.faces.facelets"
xmlns:pt="jakarta.faces.passthrough">
<f:view>
<h:form id="models_form_id" enctype="multipart/form-data">
.....
<h:inputFile id="file" value="#{myBean.files}" pt:multiple="true"/>
....
<h:commandButton id="submit" value="Submit" action="#{myBean.submit}" />
</h:form>
</f:view>
</ui:composition>
您的 bean 代码只需要支持 'files' 属性作为jakarta.servlet.http.Part
元素的列表:
@Named
@RequestScoped
public class MyBean implements Serializable {
private List<Part> files;
public List<Part> getFiles() {
return files;
}
public void setFiles(List<Part> files) {
this.files = files;
}
public void submit() throws IOException {
if (files != null) {
System.out.println(" uploading " + files.size() + " files");
for (Part file : files) {
System.out.println("name: " + file.getSubmittedFileName());
System.out.println("type: " + file.getContentType());
System.out.println("size: " + file.getSize());
InputStream content = file.getInputStream();
// Write content to disk or DB.
}
}
}
}
....
而已。 现在,您可以将上传的文件作为表单中的任何其他数据进行处理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.