如何使用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版本:
-
需要将
<h:form>
的enctype
属性设置为multipart/form-data
。 如果不存在,则ajax上传可能正常工作,但一般的浏览器行为是不确定的,并且取决于表单组成和web浏览器的版本。 只要始终指定它在安全的一面。 -
当使用
mode="advanced"
(即ajax上传,这是默认的)时,确保你在(master)模板中有一个<h:head>
。 这将确保正确包含必要的JavaScript文件。 这对于mode="simple"
(非ajax上传)不是必需的,但是这会破坏所有其他PrimeFaces组件的look'n'feel和function,所以你不想错过这个。 -
当使用
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相关的属性,如auto
, allowTypes
, update
, onstart
, oncomplete
等。 所以在这种情况下不需要指定它们。
另外请注意,您应该立即读取上述方法中的文件内容,而不是在稍后的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无关的另一个可能的原因:
-
只有当您使用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将会继续,就像没有任何事情发生一样。 -
只有当您使用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>
-
有一个嵌套的
<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>