PrimeFaces 4.0/JSF 2.2.x 中的文件上传不适用于 AJAX - javax.servlet.ServletException:请求内容类型不是多部分/表单数据
Posted
技术标签:
【中文标题】PrimeFaces 4.0/JSF 2.2.x 中的文件上传不适用于 AJAX - javax.servlet.ServletException:请求内容类型不是多部分/表单数据【英文标题】: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 【发布时间】:2013-10-16 05:47:03 【问题描述】:重要提示:此线程中讨论的问题是 fixed,截至 10 月 6 日星期一,PrimeFaces 5.1 final(社区发布)released, 2014 年(就在几分钟前)。我尝试了JSF 2.2.8-02(或api,impl)。
因此,如果您碰巧使用了该版本(或更高版本,无需提及),您甚至不需要再阅读此问题了。
我有一个正在运行的网络应用程序
GlassFish 4.0 Mojarra 2.2.4 PrimeFaces 4.0 最终版除了使用 AJAX 上传文件外,其他一切都运行良好。以下 xhtml 文件通过 PrimeFaces 命令按钮触发的 AJAX 请求发送多部分内容。
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:p="http://primefaces.org/ui"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h:form prependId="true" enctype="multipart/form-data">
<p:fileUpload id="txtCatImage"
value="#testManagedBean.uploadedFile"
mode="advanced"
dragDropSupport="true"
fileLimit="1"
sizeLimit="100000"
multiple="false"
allowTypes="/(\.|\/)(gif|jpe?g|png)$/"
fileUploadListener="#testManagedBean.fileUploadListener"/>
<p:message for="txtCatImage" showSummary="false"/>
<p:commandButton id="btnSubmit"
actionListener="#testManagedBean.insert"
ajax="true" icon="ui-icon-check" value="Save"/>
</h:form>
</h:body>
</html>
测试托管 bean:
@ManagedBean
@ViewScoped
public final class TestManagedBean implements Serializable
private static final long serialVersionUID = 1L;
private UploadedFile uploadedFile;
public TestManagedBean()
public UploadedFile getUploadedFile()
return uploadedFile;
public void setUploadedFile(UploadedFile uploadedFile)
this.uploadedFile = uploadedFile;
public void fileUploadListener(FileUploadEvent event)
uploadedFile=event.getFile();
public void insert()
if(uploadedFile!=null)
System.out.println(uploadedFile.getFileName());
else
System.out.println("The file object is null.");
从文件浏览器上传文件时,它会在其侦听器中显示文件名 - fileUploadListener()
。
上传文件后,当按下给定的命令按钮(ajax="true"
)时,会引发以下异常。
WARNING: javax.servlet.ServletException: The request content-type is not a multipart/form-data
javax.faces.FacesException: javax.servlet.ServletException: The request content-type is not a multipart/form-data
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decode(NativeFileUploadDecoder.java:44)
at org.primefaces.component.fileupload.FileUploadRenderer.decode(FileUploadRenderer.java:44)
at javax.faces.component.UIComponentBase.decode(UIComponentBase.java:831)
at javax.faces.component.UIInput.decode(UIInput.java:771)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1225)
at javax.faces.component.UIInput.processDecodes(UIInput.java:676)
at javax.faces.component.UIForm.processDecodes(UIForm.java:225)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1220)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1220)
at javax.faces.component.UIViewRoot.processDecodes(UIViewRoot.java:929)
at com.sun.faces.lifecycle.ApplyRequestValuesPhase.execute(ApplyRequestValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:70)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at filter.NoCacheFilter.doFilter(NoCacheFilter.java:28)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.Abstractiostrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:722)
Caused by: javax.servlet.ServletException: The request content-type is not a multipart/form-data
at org.apache.catalina.fileupload.Multipart.getPart(Multipart.java:187)
at org.apache.catalina.connector.Request.getPart(Request.java:4535)
at org.apache.catalina.connector.RequestFacade.getPart(RequestFacade.java:1095)
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decodeAdvanced(NativeFileUploadDecoder.java:60)
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decode(NativeFileUploadDecoder.java:37)
... 48 more
SEVERE: javax.servlet.ServletException: The request content-type is not a multipart/form-data
at org.apache.catalina.fileupload.Multipart.getPart(Multipart.java:187)
at org.apache.catalina.connector.Request.getPart(Request.java:4535)
at org.apache.catalina.connector.RequestFacade.getPart(RequestFacade.java:1095)
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decodeAdvanced(NativeFileUploadDecoder.java:60)
at org.primefaces.component.fileupload.NativeFileUploadDecoder.decode(NativeFileUploadDecoder.java:37)
at org.primefaces.component.fileupload.FileUploadRenderer.decode(FileUploadRenderer.java:44)
at javax.faces.component.UIComponentBase.decode(UIComponentBase.java:831)
at javax.faces.component.UIInput.decode(UIInput.java:771)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1225)
at javax.faces.component.UIInput.processDecodes(UIInput.java:676)
at javax.faces.component.UIForm.processDecodes(UIForm.java:225)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1220)
at javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:1220)
at javax.faces.component.UIViewRoot.processDecodes(UIViewRoot.java:929)
at com.sun.faces.lifecycle.ApplyRequestValuesPhase.execute(ApplyRequestValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:70)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at filter.NoCacheFilter.doFilter(NoCacheFilter.java:28)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:722)
只有在命令按钮的 ajax 属性设置为 false - ajax="false"
时才能工作。
我已在 Tomcat 7.0.35 中将 Mojarra 版本降级为 2.1.9。它与这个 Mojarra 版本以及 PrimeFaces 4.0 final(以及 PrimeFaces 4.0 RC1)一起使用 - 使用 AJAX 请求上传的文件。
我也尝试过以下 Mojarra 版本
2.2.0 2.2.1 2.2.2 2.2.3 2.2.4在 GlassFish 4.0 中,但他们都没有成功上传带有 AJAX 请求的文件,这是非常需要的,因为在 PrimeFaces DataTable 中使用<p:rowEditor/>
(例如每行中的图像)编辑行始终是基于 AJAX 的。
无论如何我都想保留 GlassFish 4.0。我还尝试在 GlassFish 4.0 中降级 Mojarra 2.1.9,但它未能创建以异常结尾的包。 GlassFish 4.0 似乎不适用于低于 2.2.x 的 Mojarra。
那么,是什么导致了这个异常——PrimeFaces 还是 JSF?只是困惑。在这个给定的环境中,是否有任何解决方法可以使用 AJAX 请求上传文件?
编辑:
web.xml
中的过滤器映射:
<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>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
2014 年 1 月 8 日,Mojarra 版本 2.2.5(或 api、impl)released 仍然存在问题。
在 2014 年 3 月 4 日再次尝试使用 Mojarra 版本 2.2.6(或api、impl)released。问题仍然存在。
在 2014 年 5 月 5 日的 PrimeFaces 5.0 最终 released 上仍然无效。
【问题讨论】:
我没有找到典型的答案。通过扩展原始渲染器并仅用 2 或 3 行代码覆盖decode()
方法,渲染器可以更加简化。无需复制粘贴整个原始源代码。然而,这是找到原因的好方法。
@BalusC :可以把它作为答案。
只是指出这是 PF 5.0.5 中的fixed
@HatemAlimam :我刚刚尝试过PrimeFaces 5.1.RC1。它没有问题。谢谢。
【参考方案1】:
正如 Kai 在当前问题的 his answer 中正确指出的那样,问题是由 FileUploadRenderer
使用的 NativeFileUploadDecoder
未检查请求是否为 multipart/form-data
请求引起的。当组件出现在提交“常规”ajax 请求的表单中时,这将导致麻烦。 CommonsFileUploadDecoder
可以正确检查,这就是它在 JSF 2.1 中正常工作的原因,因为它还没有本地文件上传解析器。
他使用自定义渲染器解决此问题的解决方案方向正确,但是该方法相当笨拙。在这种特殊情况下,绝对不需要复制粘贴包含 200 多行的整个类来添加更多行。相反,只需完全扩展该类,并在委托给 super 之前使用 if 检查完全覆盖该方法,如下所示:
package com.example;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.primefaces.component.fileupload.FileUploadRenderer;
public class MyFileUploadRenderer extends FileUploadRenderer
@Override
public void decode(FacesContext context, UIComponent component)
if (context.getExternalContext().getRequestContentType().toLowerCase().startsWith("multipart/"))
super.decode(context, component);
就是这样(尽管将 <renderer-kit>
条目保留在 faces-config.xml
中)。如果请求不是multipart
请求,则没有继续解码的意义。文件部分无论如何都不可用(当通过ExternalContext
可以轻松获得相同的功能时,也没有必要转换回javax.servlet.*
API)。
【讨论】:
是的,你是对的。这绝对更容易处理!感谢您发布建议。 Baluc,感谢您的发帖。我们有类似的要求,我们必须在一个表单中上传多个文件并使用 ajax 提交表单。我合并了您的代码并确实更改了 faces-config.xml 并使用了commons
应该在 web.xml 文件的context params
中定义以及上面描述的其他更改,否则当您阅读 bean 时文件的内容将是 null
。
@OlivierM.:这是 PF 4.x 中的一个已知错误。升级到 5.x 或使用 getInputStream()
。另见 a.o. ***.com/q/15659071这一切都与当前的问答无关。【参考方案2】:
我有同样的问题。它似乎与<p:commandButton>
的关系比与<p:fileUpload>
组件的关系更大,因为它与<h:commandButton>
一起使用(即使与ajax 一起使用)。
你可以试试:
<h:commandButton id="btnSubmit" actionListener="#testManagedBean.insert" value="Save">
<f:ajax execute="@all" render="@form"/>
</h:commandButton>
我不能告诉你它为什么或如何工作,但它为我解决了这个问题。 当然,缺点是,你必须自己做造型,至少直到 Primefaces 的人解决了这个问题。
编辑:
在挖掘源代码并进行一些调试后,如果发现实际上有两个请求(我在<p:wizard/>
中尝试过)。第一个是multipart/form-data
,它实际上是上传文件。它在 Bean 中触发 fileUploadEvent。我按下了向导 next 按钮,提交了另一个带有 enctype application/www-urlencoded
的表单。这会导致异常。结论是,与我在抑制异常的评论中所写的不同,这是一个有效的解决方案。这甚至可以通过不包括更改 Primefaces.jar 的方式来完成,如果这些人在未来的版本中解决了这个问题,这会很方便。
所以这里是需要做的:
创建一个新类com.yourpackage.fileupload.FileUploadRenderer
将以下代码复制并粘贴到您的新类中:
package com.yourpackage.fileupload.fileupload;
import java.io.IOException;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.servlet.http.HttpServletRequest;
import org.primefaces.component.fileupload.CommonsFileUploadDecoder;
import org.primefaces.component.fileupload.FileUpload;
import org.primefaces.component.fileupload.NativeFileUploadDecoder;
import org.primefaces.config.ConfigContainer;
import org.primefaces.context.RequestContext;
import org.primefaces.expression.SearchExpressionFacade;
import org.primefaces.renderkit.CoreRenderer;
import org.primefaces.util.HTML;
import org.primefaces.util.WidgetBuilder;
public class FileUploadRenderer extends CoreRenderer
@Override
public void decode(FacesContext context, UIComponent component)
FileUpload fileUpload = (FileUpload) component;
if (!fileUpload.isDisabled())
ConfigContainer cc = RequestContext.getCurrentInstance().getApplicationContext().getConfig();
String uploader = cc.getUploader();
boolean isAtLeastJSF22 = cc.isAtLeastJSF22();
if (uploader.equals("auto"))
if (isAtLeastJSF22)
if (isMultiPartRequest(context))
NativeFileUploadDecoder.decode(context, fileUpload);
else
CommonsFileUploadDecoder.decode(context, fileUpload);
else if (uploader.equals("native"))
if (!isAtLeastJSF22)
throw new FacesException("native uploader requires at least a JSF 2.2 runtime");
NativeFileUploadDecoder.decode(context, fileUpload);
else if (uploader.equals("commons"))
CommonsFileUploadDecoder.decode(context, fileUpload);
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException
FileUpload fileUpload = (FileUpload) component;
encodeMarkup(context, fileUpload);
if (fileUpload.getMode().equals("advanced"))
encodeScript(context, fileUpload);
protected void encodeScript(FacesContext context, FileUpload fileUpload) throws IOException
String clientId = fileUpload.getClientId(context);
String update = fileUpload.getUpdate();
String process = fileUpload.getProcess();
WidgetBuilder wb = getWidgetBuilder(context);
wb.initWithDomReady("FileUpload", fileUpload.resolveWidgetVar(), clientId, "fileupload");
wb.attr("auto", fileUpload.isAuto(), false)
.attr("dnd", fileUpload.isDragDropSupport(), true)
.attr("update", SearchExpressionFacade.resolveComponentsForClient(context, fileUpload, update), null)
.attr("process", SearchExpressionFacade.resolveComponentsForClient(context, fileUpload, process), null)
.attr("maxFileSize", fileUpload.getSizeLimit(), Long.MAX_VALUE)
.attr("fileLimit", fileUpload.getFileLimit(), Integer.MAX_VALUE)
.attr("invalidFileMessage", fileUpload.getInvalidFileMessage(), null)
.attr("invalidSizeMessage", fileUpload.getInvalidSizeMessage(), null)
.attr("fileLimitMessage", fileUpload.getFileLimitMessage(), null)
.attr("messageTemplate", fileUpload.getMessageTemplate(), null)
.attr("previewWidth", fileUpload.getPreviewWidth(), 80)
.attr("disabled", fileUpload.isDisabled(), false)
.callback("onstart", "function()", fileUpload.getOnstart())
.callback("onerror", "function()", fileUpload.getOnerror())
.callback("oncomplete", "function()", fileUpload.getOncomplete());
if (fileUpload.getAllowTypes() != null)
wb.append(",allowTypes:").append(fileUpload.getAllowTypes());
wb.finish();
protected void encodeMarkup(FacesContext context, FileUpload fileUpload) throws IOException
if (fileUpload.getMode().equals("simple"))
encodeSimpleMarkup(context, fileUpload);
else
encodeAdvancedMarkup(context, fileUpload);
protected void encodeAdvancedMarkup(FacesContext context, FileUpload fileUpload) throws IOException
ResponseWriter writer = context.getResponseWriter();
String clientId = fileUpload.getClientId(context);
String style = fileUpload.getStyle();
String styleClass = fileUpload.getStyleClass();
styleClass = styleClass == null ? FileUpload.CONTAINER_CLASS : FileUpload.CONTAINER_CLASS + " " + styleClass;
boolean disabled = fileUpload.isDisabled();
writer.startElement("div", fileUpload);
writer.writeAttribute("id", clientId, "id");
writer.writeAttribute("class", styleClass, styleClass);
if (style != null)
writer.writeAttribute("style", style, "style");
//buttonbar
writer.startElement("div", fileUpload);
writer.writeAttribute("class", FileUpload.BUTTON_BAR_CLASS, null);
//choose button
encodeChooseButton(context, fileUpload, disabled);
if (!fileUpload.isAuto())
encodeButton(context, fileUpload.getUploadLabel(), FileUpload.UPLOAD_BUTTON_CLASS, "ui-icon-arrowreturnthick-1-n");
encodeButton(context, fileUpload.getCancelLabel(), FileUpload.CANCEL_BUTTON_CLASS, "ui-icon-cancel");
writer.endElement("div");
//content
writer.startElement("div", null);
writer.writeAttribute("class", FileUpload.CONTENT_CLASS, null);
writer.startElement("table", null);
writer.writeAttribute("class", FileUpload.FILES_CLASS, null);
writer.startElement("tbody", null);
writer.endElement("tbody");
writer.endElement("table");
writer.endElement("div");
writer.endElement("div");
protected void encodeSimpleMarkup(FacesContext context, FileUpload fileUpload) throws IOException
encodeInputField(context, fileUpload, fileUpload.getClientId(context));
protected void encodeChooseButton(FacesContext context, FileUpload fileUpload, boolean disabled) throws IOException
ResponseWriter writer = context.getResponseWriter();
String clientId = fileUpload.getClientId(context);
String cssClass = HTML.BUTTON_TEXT_ICON_LEFT_BUTTON_CLASS + " " + FileUpload.CHOOSE_BUTTON_CLASS;
if (disabled)
cssClass += " ui-state-disabled";
writer.startElement("span", null);
writer.writeAttribute("class", cssClass, null);
//button icon
writer.startElement("span", null);
writer.writeAttribute("class", HTML.BUTTON_LEFT_ICON_CLASS + " ui-icon-plusthick", null);
writer.endElement("span");
//text
writer.startElement("span", null);
writer.writeAttribute("class", HTML.BUTTON_TEXT_CLASS, null);
writer.writeText(fileUpload.getLabel(), "value");
writer.endElement("span");
if (!disabled)
encodeInputField(context, fileUpload, clientId + "_input");
writer.endElement("span");
protected void encodeInputField(FacesContext context, FileUpload fileUpload, String clientId) throws IOException
ResponseWriter writer = context.getResponseWriter();
writer.startElement("input", null);
writer.writeAttribute("type", "file", null);
writer.writeAttribute("id", clientId, null);
writer.writeAttribute("name", clientId, null);
if (fileUpload.isMultiple())
writer.writeAttribute("multiple", "multiple", null);
if (fileUpload.getStyle() != null)
writer.writeAttribute("style", fileUpload.getStyle(), "style");
if (fileUpload.getStyleClass() != null)
writer.writeAttribute("class", fileUpload.getStyleClass(), "styleClass");
if (fileUpload.isDisabled())
writer.writeAttribute("disabled", "disabled", "disabled");
writer.endElement("input");
protected void encodeButton(FacesContext context, String label, String styleClass, String icon) throws IOException
ResponseWriter writer = context.getResponseWriter();
String cssClass = HTML.BUTTON_TEXT_ICON_LEFT_BUTTON_CLASS + " ui-state-disabled " + styleClass;
writer.startElement("button", null);
writer.writeAttribute("type", "button", null);
writer.writeAttribute("class", cssClass, null);
writer.writeAttribute("disabled", "disabled", null);
//button icon
String iconClass = HTML.BUTTON_LEFT_ICON_CLASS;
writer.startElement("span", null);
writer.writeAttribute("class", iconClass + " " + icon, null);
writer.endElement("span");
//text
writer.startElement("span", null);
writer.writeAttribute("class", HTML.BUTTON_TEXT_CLASS, null);
writer.writeText(label, "value");
writer.endElement("span");
writer.endElement("button");
private boolean isMultiPartRequest(FacesContext context)
if (context == null)
return false;
return ((HttpServletRequest) context.getExternalContext().getRequest()).getContentType().startsWith("multipart");
在faces-config.xml
的底部添加以下行:
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.FileUploadRenderer</renderer-type>
<renderer-class>com.yourpackage.fileupload.FileUploadRenderer</renderer-class>
</renderer>
</render-kit>
你准备好了!
我们做了什么?我们创建了自己的 FileUploadRenderer,它使用 isMultiPartRequest()
方法检查 contentType 是否真的是 multipart/form-data
。只有当这返回 true 时,才会执行其余的代码。在任何其他情况下,什么都不会发生,这意味着不会抛出异常。如果 Primefaces 修复了这个问题,您只需从 faces-config.xml
中删除这些行即可使用它们的类。
让我知道这是否适合你!
编辑
此代码检查给定请求是否为 multipart/form-data 类型。如果不是,则停止执行。原始的 Primefaces 代码无论如何都会继续。正如我上面提到的,如果您在 Primefaces 组件中上传文件,实际上会发出两个请求:
-
Ajax-FileUpload 使用
<p:fileUpload/>
(enctype:multipart/form-data
)
<p:editRow/>
或 <p:wizard/>
中的 Ajax 操作(编码类型:application/www-form-urlencoded
)
第一个由渲染器处理,而第二个导致原始代码中的异常,因为渲染器试图处理它无法处理的事情。通过代码中的更改,只有multipart/form-data
表单由渲染器处理,因此不会发生异常。 IMO 这显然是 Primefaces 来源中的一个错误。代码差异只是 private boolean isMultiPartRequest(FacesContext context)
方法及其在代码中的一次出现。很高兴能帮到你!
【讨论】:
我曾经尝试过<h:commandButton>
,就像你提到的那样,它有效,但我可以看到整个页面都提交了,因为根本没有 AJAX(在你发布这个之后我刚刚重复了同样的事情回答)。此外,在包含每行图像的 PrimeFaces 数据表中使用 <p:rowEditor/>
编辑行始终是基于 AJAX 的,我们没有 <h:commandButton>
。它根据自己的政策运作。
不幸的是,这种方法似乎只在少数情况下有效。我在<p:Wizard/>
中有一个<p:fileUpload/>
,它在那里也不起作用。我不确定虽然这是旧的并且已经回答了,但我想分享一些东西,以防你错过它: PrimeFaces 4+ 现在有一个上下文参数,您可以使用它(在 web.xml 中)手动选择应该使用哪个上传器(native-servlet3 或 commons)。您可以使用它来强制 commons-uploader 像这样:
<context-param>
<param-name>primefaces.UPLOADER</param-name>
<param-value>commons</param-value>
</context-param>
(当然,您仍然需要上面和指南中所述的 FileUploadFilter)。 有关详细信息,请参阅 PrimeFaces 用户指南。
【讨论】:
感谢 Matrium。这有帮助。 在 JSF 2.2.6 中,当我使用commons
上传器在 web.xml
中定义此 <context-param>
时,相关托管 bean 本身中的文件上传侦听器未被调用。对于auto
和native
上传器,将调用侦听器,但uploadedFile.getContents()
方法始终返回null
。我必须坚持org.apache.commons.io.IOUtils.toByteArray(uploadedFile.getInputstream())
,就像以前获取上传的文件内容一样。我还缺少其他东西吗?
@Tiny,我也遇到了同样的问题,当使用 context-param 时我的处理程序没有被调用,你找到解决方案了吗?
@ChristianGötz:遗憾的是还没有。我仍在摸索这个解决方案。目前,我正在使用org.apache.commons.io.IOUtils.toByteArray(uploadedFile.getInputstream())
从InputStream
获取一个字节数组,如果成功,我将在这里发表评论。谢谢!【参考方案4】:
@BalusC - 您对扩展现有 FileUploadRenderer
的建议非常干净。谢谢!
根据您使用的 JSF 版本,您可能会看到一个随机的 iFrame 弹出窗口。这是此处指出的错误:JAVASERVERFACES-2843
在我第一次尝试解决这个问题时(无需升级到 2.2.1),我只是用 CSS 隐藏了 iFrame。
#JSFFrameId
visibility:hidden;
这可行,但由于某种原因,其他 AJAX 提交不会触发。然后我调用了一个小脚本来删除 iFrame 并解决了问题。
<h:commandButton id="btnSubmit" action="#fileUploadController.upload" value="Save" >
<f:ajax execute="@all" render="frmMain" onevent="removeIFrame()" />
</h:commandButton>
function removeIFrame()
document.getElementById("JSFFrameId").removeNode();
【讨论】:
【参考方案5】:我有同样的问题, 在我的情况下,我在数据表中使用了 primefaces 文件上传器,尝试使用 onRowEdit 修改现有图像,最终出现上述相同的错误。 然后我将 primefaces jar 更改为 5.1 版本。现在它工作正常。
【讨论】:
以上是关于PrimeFaces 4.0/JSF 2.2.x 中的文件上传不适用于 AJAX - javax.servlet.ServletException:请求内容类型不是多部分/表单数据的主要内容,如果未能解决你的问题,请参考以下文章
Primefaces:如何在 primefaces 3.5 中为菜单栏设置粘性
将 primefaces 数据表与 org.primefaces.component.datatable.DataTable 绑定;