繁体   English   中英

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

[英]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可以使用多个文件上传。

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

即使是很久以前:考虑到你自己的评论,我会推荐像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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM