FileUpload和表单提交

时间:2018-09-24 20:08:12

标签: jsf file-upload primefaces usability omnifaces

考虑一个带有fileUpload组件的表单。用户使用fileUpload组件选择一些文件,然后提交表单,合理地认为所选文件与表单一起隐式提交,但事实并非如此。 Primefaces fileUpload组件要求用户在提交之前将文件作为显式操作上载,并为此提供一个方便的“上载”按钮。但是,如果用户忽略了此操作,则提交的表单中没有文件,也没有指示已将其排除在提交之外。在某些使用情况下,文件以后可能不会附加到表单提交创建的对象上。相比之下,JSF和Omnifaces inputFile组件以及它们所基于的HTML5 <input type="file">会在提交时上传文件,这对我来说似乎更有用。

这是一些示例代码。首先演示:

    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"
        xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:p="http://primefaces.org/ui">
    <h:head></h:head>
    <h:body>
        <f:view>
            <h:form id="form" enctype="multipart/form-data" method="POST">
                <p:panelGrid id="grid" columns="2" cellpadding="15">
                    <p:outputLabel>HTML5 input type="file"</p:outputLabel>
                    <input type="file" id="fileupload1" name="fileupload1" multiple="multiple" />
                    <p:outputLabel>Primefaces fileUpload</p:outputLabel>
                    <p:fileUpload id="fileupload2" fileUploadListener="#{myBean.handleFileUpload}" multiple="true" />
                    <p:outputLabel>JSF Input File</p:outputLabel>
                    <h:inputFile id="fileupload3" value="#{myBean.uploadedFile}" />
                </p:panelGrid>
                <p:commandButton id="submit" value="Submit" actionListener="#{myBean.submit}" process="@form" ajax="false" />
            </h:form>
        </f:view>
    </h:body>
    </html>

还有后备豆:

@ManagedBean
@SessionScoped
public class MyBean {

    private Map<String, Path> uploadedFiles = new HashMap<String, Path>();
    Part uploadedFile;

    public void handleFileUpload(FileUploadEvent event) {
        System.out.println("Uploaded file: " + event.getFile().getFileName());
    }

    public void submit(ActionEvent actionEvent) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
        for (Part part : request.getParts()) {
            if (part != null && part.getSize() > 0 && part.getSubmittedFileName() != null) {
                String fileName = part.getSubmittedFileName();
                String contentType = part.getContentType();
                System.out.println("File in request: " + fileName + "; contentType " + contentType + "; size: " + part.getSize());
            }
        }
        System.out.println("Primefaces uploaded Files (" + (uploadedFiles == null ? "0" : uploadedFiles.size()) + "):");
        if (uploadedFiles != null) {
            for (String name : uploadedFiles.keySet()) {
                System.out.println("     Name: " + name + "; Path " + uploadedFiles.get(name));
            }
        }
        System.out.println("JSF uploaded file: " + (uploadedFile == null ? "null" : uploadedFile.getSubmittedFileName() + ", size " + uploadedFile.getSize()));
    }

    public Part getUploadedFile() {
        System.out.println("get uploaded File Part: " + (uploadedFile == null ? "none" : uploadedFile.getSubmittedFileName()));
        return uploadedFile;
    }

    public void setUploadedFile(Part uploadedFile) {
        System.out.println("set uploaded File Part: " + (uploadedFile == null ? "none" : uploadedFile.getSubmittedFileName()));
        this.uploadedFile = uploadedFile;
    }
}

如果在所有三个文件上传组件中选择文件后提交此表单,则Primefaces是唯一一个在提交时不上传文件的表单。当然,您可以将“ auto”设置为true,以使其在选定时上传文件,但是如果用户决定不上传文件,则无法撤消。您可以将其设置为必填,但通常情况下,用户可能不选择上载任何文件。因此,Primefaces组件似乎在这方面倒退了一步,尽管它非常易于使用并且功能更全面。

是否有Primefaces方法在提交时丢失我上载的多个文件?

1 个答案:

答案 0 :(得分:0)

感谢@Kukeltje帮助我找到答案。

“简单”文件上传小部件,例如HTML5 <input type="file">,JSF <h:inputFile>和Omnifaces <o:inputFile>,允许用户在提交时选择一个或多个要上传的文件。

一些更复杂的fileUpload小部件,例如Primefaces(带有mode="advanced",默认模式和multiple="true"),允许用户在提交时间之前一键上传多个文件,但是如果用户忽略了为此,所选文件不会在提交时上传。如果设计人员希望避免可用性问题,即用户假定所选文件在提交时上载而没有上载,则PF小部件可以与mode="simple"一起使用。以这种方式使用PF小部件的好处是美观,并且可以选择多个文件。

作为一个脚注,与原始问题无关,特别是PF小部件存在问题,在相似框架中可能还有其他相似问题。在简单模式下,如果用户选择多个文件,则小部件仅显示第一个文件。

我看到另一个问题,在用户在允许多个窗口小部件的任何一个中选择了多个文件之后,无法取消选择文件。在尝试使用JS列出所选文件后,我发现由于浏览器的安全性,我无法在JS中更改列表,请参见BalusC的答案here。我最后得到了一些简单的JS,在提交时检查了PF上载小部件是否有任何选定的文件,如果有,则阻止提交并要求用户在提交前上载文件。我认为这是最有用的解决方案。

<p:commandButton value="Submit" actionListener="#{bean.submit}" onclick="return checkAttachments();" ajax="false" />

<script>
function checkAttachments() {
    var attachmentRows = $(".ui-fileupload-row");
    console.log("rows " + attachmentRows.length);
    if (attachmentRows.length == 0 ) {
        return true;
    }
    PF('uploadAttachmentsWarningDialog').show();
    return false;
}
</script>