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(或apiimpl)。

因此,如果您碰巧使用了该版本(或更高版本,无需提及),您甚至不需要再阅读此问题了。


我有一个正在运行的网络应用程序

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 中使用&lt;p:rowEditor/&gt;(例如每行中的图像)编辑行始终是基于 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);
        
    


就是这样(尽管将 &lt;renderer-kit&gt; 条目保留在 faces-config.xml 中)。如果请求不是multipart 请求,则没有继续解码的意义。文件部分无论如何都不可用(当通过ExternalContext 可以轻松获得相同的功能时,也没有必要转换回javax.servlet.* API)。

【讨论】:

是的,你是对的。这绝对更容易处理!感谢您发布建议。 Baluc,感谢您的发帖。我们有类似的要求,我们必须在一个表单中上传多个文件并使用 ajax 提交表单。我合并了您的代码并确实更改了 faces-config.xml 并使用了 ,但没有调用上传侦听器。请让我知道我哪里出错了。问候,哨兵 @user1503117:这是一个不同的问题。前往***.com/questions/8875818/… 如果您仍然卡住,请提出一个新问题。 commons 应该在 web.xml 文件的context params 中定义以及上面描述的其他更改,否则当您阅读 bean 时文件的内容将是 null @OlivierM.:这是 PF 4.x 中的一个已知错误。升级到 5.x 或使用 getInputStream()。另见 a.o. ***.com/q/15659071这一切都与当前的问答无关。【参考方案2】:

我有同样的问题。它似乎与&lt;p:commandButton&gt; 的关系比与&lt;p:fileUpload&gt; 组件的关系更大,因为它与&lt;h:commandButton&gt; 一起使用(即使与ajax 一起使用)。

你可以试试:

<h:commandButton id="btnSubmit" actionListener="#testManagedBean.insert" value="Save">
    <f:ajax execute="@all" render="@form"/>
</h:commandButton> 

我不能告诉你它为什么或如何工作,但它为我解决了这个问题。 当然,缺点是,你必须自己做造型,至少直到 Primefaces 的人解决了这个问题。

编辑

在挖掘源代码并进行一些调试后,如果发现实际上有两个请求(我在&lt;p:wizard/&gt; 中尝试过)。第一个是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 使用 &lt;p:fileUpload/&gt;(enctype:multipart/form-data&lt;p:editRow/&gt;&lt;p:wizard/&gt; 中的 Ajax 操作(编码类型:application/www-form-urlencoded

第一个由渲染器处理,而第二个导致原始代码中的异常,因为渲染器试图处理它无法处理的事情。通过代码中的更改,只有multipart/form-data 表单由渲染器处理,因此不会发生异常。 IMO 这显然是 Primefaces 来源中的一个错误。代码差异只是 private boolean isMultiPartRequest(FacesContext context) 方法及其在代码中的一次出现。很高兴能帮到你!

【讨论】:

我曾经尝试过&lt;h:commandButton&gt;,就像你提到的那样,它有效,但我可以看到整个页面都提交了,因为根本没有 AJAX(在你发布这个之后我刚刚重复了同样的事情回答)。此外,在包含每行图像的 PrimeFaces 数据表中使用 &lt;p:rowEditor/&gt; 编辑行始终是基于 AJAX 的,我们没有 &lt;h:commandButton&gt; 。它根据自己的政策运作。 不幸的是,这种方法似乎只在少数情况下有效。我在&lt;p:Wizard/&gt; 中有一个&lt;p:fileUpload/&gt;,它在那里也不起作用。我不确定。也许你可以为 Primefaces 的人提交一个错误。看起来他们很难完成 我在 PrimeFaces 论坛上发过同样的帖子。部分solution 已发布在那里,但很难更新 jar 中的代码(我不知道如何打包/解包和更改 jar 文件中的代码),即使这样做也可以工作 - 它可能也可能不会。 我可能错了,但 Primefaces 论坛中的建议是抑制抛出的异常。我不认为这会使错误的原因神奇地消失。我今天将开始挖掘代码。如果我能够解决问题,我会发布解决方案。 这确实在问题中提到的给定环境中工作 - 可以使用 AJAX 提交上传文件。非常感谢。【参考方案3】:

虽然这是旧的并且已经回答了,但我想分享一些东西,以防你错过它: 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 中定义此 &lt;context-param&gt; 时,相关托管 bean 本身中的文件上传侦听器未被调用。对于autonative 上传器,将调用侦听器,但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> 

javascript

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 绑定;

实现PrimeFaces扩展时出错

使用ajax从jsf中的primefaces树选定项设置primefaces selectOneMenu默认值

JSF 2 PrimeFaces 安装

在 PrimeFaces 中添加 Angular Js - JSF