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