jsf - 如何使用PrimeFaces p:fileUpload?永远不会调用监听器方法,或者UploadedFile为null/引发错误/不可用

标签 jsf file-upload jsf-2 primefaces

我正在尝试使用PrimeFaces上传文件,但在上传完成后未调用fileUploadListener方法。

这是视图:

<h:form>
    <p:fileUpload fileUploadListener="#{fileUploadController.handleFileUpload}"
        mode="advanced" 
        update="messages"
        sizeLimit="100000" 
        allowTypes="/(\.|\/)(gif|jpe?g|png)$/"/>

    <p:growl id="messages" showDetail="true"/>
</h:form>


和豆:

@ManagedBean
@RequestScoped
public class FileUploadController {

    public void handleFileUpload(FileUploadEvent event) {
        FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + " is uploaded.");
        FacesContext.getCurrentInstance().addMessage(null, msg);
    }

}


我在方法上放置了一个断点,但从未调用过。使用mode="simple"ajax="false"时,它已被调用,但我希望它在高级模式下工作。我正在使用Netbeans和Glassfish 3.1。

最佳答案

<p:fileUpload>的配置和故障排除方式取决于PrimeFaces版本。

所有PrimeFaces版本

以下要求适用于所有PrimeFaces版本:


enctype<h:form>属性需要设置为multipart/form-data。如果不存在,ajax上传可能就可以了,但是一般的浏览器行为是不确定的,并且取决于表单的组成和Web浏览器的版本。只要始终将其指定为安全起见即可。
使用mode="advanced"时(即ajax上传,这是默认设置),然后确保(主)模板中有<h:head>。这将确保正确包含必要的JavaScript文件。对于mode="simple"(非ajax上传)而言,这不是必需的,但这会破坏所有其他PrimeFaces组件的外观和功能,因此无论如何您都不希望错过。
使用mode="simple"(即非ajax上传)时,必须在所有PrimeFaces命令按钮/链接上使用ajax="false"禁用ajax,并且必须将<p:fileUpload value><p:commandButton action>而不是<p:fileUpload fileUploadListener>一起使用(对于PrimeFaces < = 7.x)或<p:fileUpload listener>(对于PrimeFaces> = 8.x)


因此,如果您想要(自动)使用ajax支持上传文件(请注意<h:head>!):

<h:form enctype="multipart/form-data">
    <p:fileUpload fileUploadListener="#{bean.upload}" auto="true" /> // for PrimeFaces >= 8.x this should be listener instead of fileUploadListener 
</h:form>




public void upload(FileUploadEvent event) {
    UploadedFile uploadedFile = event.getFile();
    String fileName = uploadedFile.getFileName();
    String contentType = uploadedFile.getContentType();
    byte[] contents = uploadedFile.getContents(); // Or getInputStream()
    // ... Save it, now!
}


或者,如果您要上传非ajax文件:

<h:form enctype="multipart/form-data">
    <p:fileUpload mode="simple" value="#{bean.uploadedFile}" />
    <p:commandButton value="Upload" action="#{bean.upload}" ajax="false" />
</h:form>




private UploadedFile uploadedFile; // +getter+setter

public void upload() {
    String fileName = uploadedFile.getFileName();
    String contentType = uploadedFile.getContentType();
    byte[] contents = uploadedFile.getContents(); // Or getInputStream()
    // ... Save it, now!
}


请注意,在auto中将忽略与Ajax相关的属性,例如allowTypesupdateonstartoncompletemode="simple"等。因此,在这种情况下无需指定它们。

还要注意,您应该立即在上述方法内部读取文件内容,而不是在以后的HTTP请求调用的其他bean方法中读取文件内容。这是因为上载的文件内容是请求范围的,因此在以后的/不同的HTTP请求中不可用。任何在以后的请求中尝试读取它的尝试都很有可能在临时文件上以java.io.FileNotFoundException结尾。



PrimeFaces 8.x

配置与下面的5.x版本信息相同,但是如果未调用您的侦听器,请检查属性是否被称为listener而不是(与8.x以前的版本一样)fileUploadListener

PrimeFaces 5.x

如果您使用的是JSF 2.2,并且您的faces-config.xml也声明为符合JSF 2.2版本,则不需要任何其他配置。您根本不需要PrimeFaces文件上传过滤器。如果您不清楚如何根据所使用的目标服务器正确安装和配置JSF,请转至How to properly install and configure JSF libraries via Maven?"Installing JSF" section of our JSF wiki page

但是,如果您尚未使用JSF 2.2并且无法升级(当已经在Servlet 3.0兼容容器中时应该毫不费力),则需要在web.xml中手动注册以下PrimeFaces文件上传过滤器(它将解析多部分请求并填充常规请求参数映射,以便FacesServlet可以照常继续工作):

<filter>
    <filter-name>primeFacesFileUploadFilter</filter-name>
    <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>primeFacesFileUploadFilter</filter-name>
    <servlet-name>facesServlet</servlet-name>
</filter-mapping>


<servlet-name>facesServlet值必须与同一<servlet>javax.faces.webapp.FacesServletweb.xml条目中的值完全匹配。所以如果是Faces Servlet,则需要进行相应的编辑以匹配。



PrimeFaces 4.x

与PrimeFaces 5.x相同的故事也适用于4.x。

通过UploadedFile#getContents()获取上载的文件内容仅存在潜在问题。使用本地API代替Apache Commons FileUpload时,它将返回null。您需要改用UploadedFile#getInputStream()。另见How to insert uploaded image from p:fileUpload as BLOB in MySQL?

本机API的另一个潜在问题将是,当上载组件以某种形式存在时,在该表单上会触发不处理上载组件的不同“常规” ajax请求。另请参见File upload doesn't work with AJAX in PrimeFaces 4.0/JSF 2.2.x - javax.servlet.ServletException: The request content-type is not a multipart/form-data

通过切换到Apache Commons FileUpload也可以解决这两个问题。有关详细信息,请参见PrimeFaces 3.x部分。



PrimeFaces 3.x

该版本不支持JSF 2.2 / Servlet 3.0本机文件上传。您需要手动安装Apache Commons FileUpload,并在web.xml中显式注册文件上传过滤器。

您需要以下库:


commons-fileupload.jar
commons-io.jar


这些必须存在于webapp的运行时类路径中。使用Maven时,请确保它们至少在运行时范围内(默认的编译范围也不错)。手动携带JAR时,请确保将它们放在/WEB-INF/lib文件夹中。

可以在上面的PrimeFaces 5.x部分中找到文件上传过滤器注册的详细信息。如果您使用的是PrimeFaces 4+,并且想显式使用Apache Commons FileUpload而不是JSF 2.2 / Servlet 3.0本机文件上传,那么您需要在上述库旁边并在web.xml中过滤以下上下文参数:

<context-param>
    <param-name>primefaces.UPLOADER</param-name>
    <param-value>commons</param-value><!-- Allowed values: auto, native and commons. -->
</context-param>




故障排除

如果仍然无法正常工作,则还有另一个与PrimeFaces配置无关的可能原因:


仅当您使用PrimeFaces文件上传过滤器时:您的Web应用程序中还有一个Filter,它在PrimeFaces文件上传过滤器之前运行,并且已经消耗了请求正文,例如调用getParameter()getParameterMap()getReader()等。一个请求正文只能被解析一次。当您在文件上传过滤器执行其工作之前调用这些方法之一时,文件上传过滤器将获得一个空的请求正文。

要解决此问题,您需要将文件上传过滤器的<filter-mapping>放在web.xml中的另一个过滤器之前。如果该请求不是multipart/form-data请求,则文件上传过滤器将继续运行,好像什么都没发生。如果您使用因使用注解而自动添加的过滤器(例如PrettyFaces),则可能需要通过web.xml添加显式排序。见How to define servlet filter order of execution using annotations in WAR
仅在使用PrimeFaces文件上传过滤器时:Web应用程序中还有一个Filter,它在PrimeFaces文件上传过滤器之前运行,并已执行RequestDispatcher#forward()调用。通常,URL重写过滤器(例如PrettyFaces)会执行此操作。这将触发FORWARD调度程序,但默认情况下,筛选器仅在REQUEST调度程序上侦听。

要解决此问题,您需要将PrimeFaces文件上载过滤器放在转发过滤器之前,或者重新配置PrimeFaces文件上载过滤器以监听FORWARD调度程序:

<filter-mapping>
    <filter-name>primeFacesFileUploadFilter</filter-name>
    <servlet-name>facesServlet</servlet-name>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

有一个嵌套的<h:form>。这在HTML中是非法的,并且未指定浏览器行为。浏览器通常不会在提交时发送预期的数据。确保您没有嵌套<h:form>。完全与表单的enctype无关。只是根本不嵌套表格。


如果仍然有问题,请调试HTTP流量。打开网络浏览器的开发人员工具集(在Chrome / Firebug23 + / IE9 +中按F12键),然后检查“网络/网络”部分。如果HTTP部分看起来不错,则调试JSF代码。在FileUploadRenderer#decode()上放置一个断点,然后从那里前进。



保存上传的文件

在最终使它起作用之后,您的下一个问题可能类似于“如何/在何处保存上传的文件?”。好吧,继续这里:How to save uploaded file in JSF

关于jsf - 如何使用PrimeFaces p:fileUpload?永远不会调用监听器方法,或者UploadedFile为null/引发错误/不可用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62020818/

相关文章:

javascript - 在 ASP.NET MVC 3 中上传文件而不刷新页面

javascript - 具有多个文件上传功能的 PHP 表单

java - 如何检查日期对象的空值

jsf-2 - 处理 ViewExireException/ajax 并显示 Primefaces 对话框

jsf - 如何确定代码中的当前 JSF 生命周期阶段

jsf - 在转换器中注入(inject) managedBean

c# - 如何发送 Microsoft Graph 上传 session 的最终字节?

jsf - 绑定(bind)h :selectBooleanCheckbox value to int/Integer instead of boolean/Boolean

java - @SessionScoped 抛出异常。 (生命周期异常 - "UserSession is annotated with an invalid scope")

java - 使用 JSF 2.0/Facelets,有没有办法将全局监听器附加到所有 AJAX 调用?