如何使用PrimeFaces p:fileUpload? Listener方法从不调用,或UploadedFile为空/抛出错误/不可用

我正在尝试使用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。

如何configuration和解决<p:fileUpload>取决于PrimeFaces版本。

所有PrimeFaces版本

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

  1. 需要将<h:form>enctype属性设置为multipart/form-data 。 如果不存在,则ajax上传可能正常工作,但一般的浏览器行为是不确定的,并且取决于表单组成和web浏览器的版本。 只要始终指定它在安全的一面。

  2. 当使用mode="advanced" (即ajax上传,这是默认的)时,确保你在(master)模板中有一个<h:head> 。 这将确保正确包含必要的JavaScript文件。 这对于mode="simple" (非ajax上传)不是必需的,但是这会破坏所有其他PrimeFaces组件的look'n'feel和function,所以你不想错过这个。

  3. 当使用mode="simple" (即非ajax上传)时,必须在任何PrimeFaces命令button/链接上通过ajax="false"禁用ajax="false" ,并且必须使用<p:fileUpload value><p:commandButton action>而不是<p:fileUpload fileUploadListener>

所以,如果你想(自动)file upload与Ajax支持(介意<h:head> !):

 <h:form enctype="multipart/form-data"> <p:fileUpload fileUploadListener="#{bean.upload}" auto="true" /> </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! } 

或者如果你想要非ajaxfile upload:

 <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! } 

请注意,在mode="simple"忽略了与ajax相关的属性,如autoallowTypesupdateonstartoncomplete等。 所以在这种情况下不需要指定它们。

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


PrimeFaces 5.x

如果您使用的是JSF 2.2,则不需要任何其他configuration,而且您的faces-config.xml也被声明为符合JSF 2.2版本。 根本不需要PrimeFacesfile uploadfilter。 如果您不清楚如何根据所使用的目标服务器正确安装和configurationJSF ,请转到如何通过Maven正确安装和configurationJSF库? 和我们的JSF wiki页面上的“安装JSF”部分 。

如果你还没有使用JSF 2.2,并且你不能升级它(当你已经在一个Servlet 3.0兼容的容器上时,应该毫不费力),那么你需要在web.xml手动注册下面的PrimeFacesfile uploadfilterparsing多部分请求并填充常规请求参数映射,以便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> 

facesServlet<servlet-name>值必须完全匹配相同web.xml javax.faces.webapp.FacesServlet<servlet>条目中的值。 所以,如果它是例如Faces Servlet ,那么你需要相应地编辑它以匹配。


PrimeFaces 4.x

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

通过UploadedFile#getContents()获取上传的文件内容只有一个潜在的问题。 当使用本地API代替Apache Commons FileUpload时,这将返回null 。 你需要使用UploadedFile#getInputStream()来代替。 另请参见如何在p:fileUpload中将上传的图像作为BLOB插入到MySQL中?

本地API的另一个潜在的问题是,当上传组件存在于一个表单上,在这个表单上,不同的“常规”的Ajax请求被触发,而不处理上传组件。 另请参见PrimeFaces 4.0 / JSF 2.2.x中的文件上载不适用于AJAX – javax.servlet.ServletException:请求内容types不是多部分/表单数据 。

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


PrimeFaces 3.x

此版本不支持JSF 2.2 / Servlet 3.0本机file upload。 您需要手动安装Apache Commons FileUpload,并在web.xml显式注册文件上载filter。

您需要以下库:

  • commons-fileupload.jar
  • commons-io.jar

这些必须存在于webapp的运行时类path中。 在使用Maven时,确保它们至less是运行时范围(编译的默认范围也是好的)。 当手动携带JAR时,确保它们在/WEB-INF/lib文件夹中。

file uploadfilter注册细节可以在上面的PrimeFaces 5.x部分find。 如果你使用的PrimeFaces 4+,你想明确使用Apache Commons FileUpload而不是JSF 2.2 / Servlet 3.0本地file upload,那么你需要旁边提到的库,并过滤web.xml的下面的上下文参数:

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

故障排除

如果它仍然不起作用,这里是与PrimeFacesconfiguration无关的另一个可能的原因:

  1. 只有当您使用PrimeFacesfile uploadfilter时:您的webapp中还有另一个Filter ,它 PrimeFacesfile uploadfilter之前运行并且已经通过例如调用getParameter()getParameterMap()getReader()等消耗了请求体。 请求主体只能被parsing一次。 在file uploadfilter执行其作业之前调用其中一种方法时,file uploadfilter将获得一个空的请求主体。

    要解决这个问题,你需要把file uploadfilter的<filter-mapping>放在web.xml的其他filter之前 。 如果请求不是multipart/form-data请求,那么文件上载filter将会继续,就像没有任何事情发生一样。

  2. 只有当您使用PrimeFacesfile uploadfilter时:您的Web应用Filter中还有另一个Filter ,它 PrimeFaces文件上载filter之前运行,并执行了RequestDispatcher#forward()调用。 通常,像PrettyFaces这样的URL重写filter就是这样做的。 这会触发FORWARD调度程序,但filter仅在REQUEST调度程序上默认侦听。

    为了解决这个问题,你需要把PrimeFacesfile uploadfilter放在转发filter之前 ,或者重新configurationPrimeFacesfile uploadfilter来在FORWARD调度器上进行监听:

     <filter-mapping> <filter-name>primeFacesFileUploadFilter</filter-name> <servlet-name>facesServlet</servlet-name> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> 
  3. 有一个嵌套的<h:form> 。 这在HTML中是非法的,并且浏览器行为是未指定的。 浏览器通常不会在提交时发送预期的数据。 确保你没有嵌套<h:form> 。 这完全与表单的enctype无关。 根本不要嵌套forms。

如果您仍然遇到问题,那么请debuggingHTTPstream量。 打开网页浏览器的开发者工具集(在Chrome / Firebug23 + / IE9 +中按F12),然后检查networking/networking部分。 如果HTTP部分看起来不错,那么debuggingJSF代码。 在FileUploadRenderer#decode()上放置一个断点并从那里前进。


保存上传的文件

在你终于开始工作之后,你的下一个问题可能应该就像“我如何保存上传的文件?”。 那么,继续这里: 如何保存在JSF中上传的文件 。

你也使用漂亮的脸? 然后将调度程序设置为FORWARD:

 <filter-mapping> <filter-name>PrimeFaces FileUpload Filter</filter-name> <servlet-name>Faces Servlet</servlet-name> <dispatcher>FORWARD</dispatcher> </filter-mapping> 

我注意到Primefaces 3.4和Netbeans 7.2的一点:

删除函数handleFileUpload的Netbeans自动填充参数即(事件),否则事件可能为空。

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

看起来像javax.faces.SEPARATOR_CHAR不能等于_

我对primefaces 5.3也有同样的问题,而且我经历了BalusC所描述的所有点,没有任何结果。 我遵循他的debuggingFileUploadRenderer#decode()的build议,我发现我的web.xml是不正确的设置

 <context-param> <param-name>primefaces.UPLOADER</param-name> <param-value>auto|native|commons</param-value> </context-param> 

参数值必须是这3个值中的1个,但不是全部! 整个上下文参数部分可以被删除,默认是自动的

这两个build议都没有帮助我。 所以我不得不debuggingprimefaces,发现问题的原因是:

 java.lang.IllegalStateException: No multipart config for servlet fileUpload 

然后,我在web.xml中添加了一部分到我面前的servlet。 这样就解决了这个问题:

 <servlet> <servlet-name>main</servlet-name> <servlet-class>org.apache.myfaces.webapp.MyFacesServlet</servlet-class> <load-on-startup>1</load-on-startup> <multipart-config> <location>/tmp</location> <max-file-size>20848820</max-file-size> <max-request-size>418018841</max-request-size> <file-size-threshold>1048576</file-size-threshold> </multipart-config> </servlet> 

我有同样的问题,由于我有这个post中描述的所有configuration的事实,但在我的情况是因为我有两个jQuery导入(其中之一是primefaces的查询)导致冲突上载文件。

见Primefaces jQuery的冲突

bean.xhtml

  <h:form enctype="multipart/form-data"> <p:outputLabel value="Choose your file" for="submissionFile" /> <p:fileUpload id="submissionFile" value="#{bean.file}" fileUploadListener="#{bean.uploadFile}" mode="advanced" auto="true" dragDropSupport="false" update="messages" sizeLimit="100000" fileLimit="1" allowTypes="/(\.|\/)(pdf)$/" /> </h:form> 

Bean.java

 @ManagedBean 

@ViewScoped公共类提交实现Serializable {

 private UploadedFile file; //Gets //Sets public void uploadFasta(FileUploadEvent event) throws FileNotFoundException, IOException, InterruptedException { String content = IOUtils.toString(event.getFile().getInputstream(), "UTF-8"); String filePath = PATH + "resources/submissions/" + nameOfMyFile + ".pdf"; MyFileWriter.writeFile(filePath, content); FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, event.getFile().getFileName() + " is uploaded.", null); FacesContext.getCurrentInstance().addMessage(null, message); } 

}

web.xml中

  <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> <filter> <filter-name>PrimeFaces FileUpload Filter</filter-name> <filter-class>org.primefaces.webapp.filter.FileUploadFilter</filter-class> </filter> <filter-mapping> <filter-name>PrimeFaces FileUpload Filter</filter-name> <servlet-name>Faces Servlet</servlet-name> </filter-mapping>