28

Important : The issue which is discussed in this thread has been fixed as of PrimeFaces 5.1 final (community release) released on Monday, October 6, 2014 (just a few minutes ago from now). I attempted on JSF 2.2.8-02 (or api, impl).

As such, if you happened to use that version (or higher, no need to mention), you would not even need to read this question anymore.


I have a web application running on

  • GlassFish 4.0
  • Mojarra 2.2.4
  • PrimeFaces 4.0 final

Everything except file upload with AJAX works well. The following xhtml file sends multipart contents via an AJAX request triggered by a PrimeFaces command button.

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

The test managed 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.");
        }
    }
}

When a file is uploaded from a file browser, it shows the file name in its listener - fileUploadListener().

After uploading a file, when the given command button is pressed (ajax="true"), it causes the following exception to be thrown.

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)

It can only work, when the ajax attribute of the command button is set to false - ajax="false".


I have downgraded the Mojarra version to 2.1.9 in Tomcat 7.0.35. It worked with this Mojarra version along with PrimeFaces 4.0 final (and PrimeFaces 4.0 RC1 too) - files uploaded with an AJAX request.

I have alternatively tried on the following Mojarra versions

  • 2.2.0
  • 2.2.1
  • 2.2.2
  • 2.2.3
  • 2.2.4

in GlassFish 4.0 but none of them succeeded to upload files with an AJAX request which is highly required because editing of rows using <p:rowEditor/> (along with images in each row, for example) in PrimeFaces DataTable is always AJAX based.

I want to keep GlassFish 4.0 anyhow. I have also tried to downgrade Mojarra 2.1.9 in GlassFish 4.0 but it failed to create bundles ending with an exception. GlassFish 4.0 doesn't seem to work with Mojarra lower than 2.2.x.

So, what is responsible for causing this exception - PrimeFaces or JSF? Just confused. Is there any workaround to upload files with AJAX requests in this given environment?


EDIT:

Filter mapping in 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>

The problem remains stationary along with the Mojarra version 2.2.5 (or api, impl) released on January 08, 2014.


Once again tried on the Mojarra version 2.2.6 (or api, impl) released on March 04, 2014. The problem remains intact.


Still does not work on PrimeFaces 5.0 final released on May 05, 2014.

Tiny
  • 24,933
  • 92
  • 299
  • 571
  • 1
    I don't find the answer exemplary. The renderer can be much more simplified by just extending the original renderer and overriding only the `decode()` method with 2 or 3 lines of code. No need to copypaste the whole original source code. It's however a good find of the cause. – BalusC Nov 02 '13 at 20:50
  • @BalusC : May put that as an answer. – Tiny Nov 02 '13 at 20:58
  • 1
    Just to point out that this is [fixed](https://code.google.com/p/primefaces/issues/detail?id=6925) in PF 5.0.5 – Hatem Alimam Sep 30 '14 at 15:14
  • 1
    @HatemAlimam : I have just tried on [PrimeFaces 5.1.RC1](http://www.primefaces.org/downloads). It worked with no issue. Thanks. – Tiny Sep 30 '14 at 17:52

5 Answers5

52

As Kai rightfully pointed out in his answer on the current question, the problem is caused by the NativeFileUploadDecoder as used by FileUploadRenderer not checking whether the request is a multipart/form-data request or not. This will cause trouble when the component is present in a form on which a "regular" ajax request is being submitted. The CommonsFileUploadDecoder checks that correctly and that's why it works correctly in JSF 2.1 which didn't have a native file upload parser yet.

His solution of workarounding this with a custom renderer is in the right direction, however the approach is quite clumsy. There's in this particular case absolutely no need of copypasting the whole class consisting more than 200 lines just to add a few more lines. Instead, just extend exactly that class and override exactly the method with an if check before delegating to the super as follows:

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);
        }
    }

}

That's it (keep that <renderer-kit> entry in faces-config.xml though). There's no point of continuing the decode if the request isn't a multipart request. The file parts wouldn't be available anyway (and there's also no point of casting back to javax.servlet.* API when the same functionality is readily available via ExternalContext).

Community
  • 1
  • 1
BalusC
  • 992,635
  • 352
  • 3,478
  • 3,452
  • Yep, you're right. This is definitely easier to handle! Thanks for posting the advice. – Kai Nov 04 '13 at 20:17
  • Baluc, Thanks for posting. We have similar requirement that we have to upload multiple files in a form and submit the form using ajax. I incorporated your code and did changed faces-config.xml , and used , but the upload listener is not getting called.Please lemme know where I am going wrong.Regards, Sentil – user1503117 Nov 17 '13 at 18:36
  • @user1503117: that's a different problem. Head to http://stackoverflow.com/questions/8875818/how-to-use-primefaces-pfileupload-listener-method-is-never-invoked/8880083#8880083 If you still stucks, just ask a new question. – BalusC Nov 17 '13 at 19:52
  • `commons` should be defined in `context params` of web.xml file along with other changes described above, else content of the file will be `null` when you read your bean. – Azfar Niaz Mar 28 '14 at 10:07
  • That worked 100% for me. JSF 2.2 with PrimeFaces 5.0 in a WildFly 8.0.0 server. – Rasshu Jun 17 '14 at 22:01
  • Hi BaluC Thanks for your code.I am using PF 4 + Mojarra 2.2.6 + tomahawk20 for subform + jdk 1.6.0_45. On opening a submenu item, it opens up the form with multipart/ and the subform , all in the same form, the upload is not working with your code, but the exeption javax.faces.FacesException: javax.servlet.ServletException: org.apache.tomcat.util.http.fileupload.FileUploadBase$InvalidContentTypeException: the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is application/x-www-form-urlencoded; charset=UTF-8 is avoided. please help. Sentil – user1503117 Aug 29 '14 at 07:52
  • When implementing this solution FileUploadEvent.getFile.getContents() returns null. For me the solution was to call FileUploadEvent.getFile.getInputStream and then convert the stream to byte array. – Olivier Masseau Nov 06 '15 at 14:53
  • 1
    @OlivierM.: that's a known bug in PF 4.x. Upgrade to 5.x or use `getInputStream()`. See also a.o. http://stackoverflow.com/q/15659071 This all has got nothing to do with the current Q&A. – BalusC Nov 06 '15 at 14:55
34

I had the same issue. It seems to be more related to the <p:commandButton> than the <p:fileUpload> component, since it works with an <h:commandButton> (even with ajax).

You could try:

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

I cannot tell you why or how it works, but it fixed the issue for me. The downside of course is, that you have to do the styling by yourself, at least until the Primefaces guys fix this issue.

EDIT:

After digging in the sources and doing some debugging if figured out that there are actually two requests made (I tried in <p:wizard/>). The first one is the multipart/form-data one which actually does the file upload. It fires the fileUploadEvent in the Bean. I the wizards next button is pressed another form with enctype application/www-urlencoded is submitted. This causes the exception. Conclusion is, that unlike what I wrote in the comment suppressing the exception is a valid solution. This can even be done in a way that does not include changing the Primefaces.jar which is handy if the guys fix the issue in a future version.

So here is what needs to be done:

  • Create a new class com.yourpackage.fileupload.FileUploadRenderer
  • Copy and paste the following code inside your new class:

    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");
        }
    }
    
  • Add the following lines at the bottom of your 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>
    
  • You're ready to go!

What have we done? We created our own FileUploadRenderer which checks if the contentType is really multipart/form-data using the method isMultiPartRequest(). Only if this returns true the rest of the code is executed. In any other case nothing will happen which means that no exception will be thrown. If Primefaces fixes this issue you just need to remove the lines from your faces-config.xml to use their class.

Let me know if that works for you!

EDIT

This code checks wether or not the given request is of type multipart/form-data. If it is not the execution is stopped. The original Primefaces code would continue anyway. As I mentioned above if you upload a file inside a Primefaces component there are actually two requests made:

  1. The Ajax-FileUpload using the <p:fileUpload/> (enctype: multipart/form-data)
  2. The Ajax action in the <p:editRow/> or <p:wizard/> (enctype: application/www-form-urlencoded)

The first one is handled by the renderer while the second one causes the exception in the original code since the renderer tries to handle something which it is not capable of. With the changes made in the code only multipart/form-data forms are handled by the renderer so no exceptions occur. IMO it is clearly a bug in the sources of Primefaces. The code differences is just the method private boolean isMultiPartRequest(FacesContext context) and its one occurrence in the code. Glad I could help you!

Tiny
  • 24,933
  • 92
  • 299
  • 571
Kai
  • 1,350
  • 13
  • 23
  • I once tried with `` exactly as you mentioned and it worked but I could see the whole page is submitted as there is no AJAX at all (I have just repeated the same thing after you posted this answer). Moreover, Editing of rows using `` in a PrimeFaces DataTable that holds images in each row is always AJAX based and we don't have `` for that. It works on its own policy. – Tiny Oct 15 '13 at 16:49
  • Unfortunately it seems that this approach only works in just a few cases. I have a `` inside a `` and it does not work there either. I was not sure about the . Maybe you could file a bug for the guys at Primefaces. It is looking like they are having a hard time getting the finished. – Kai Oct 16 '13 at 05:14
  • I had the same post on the PrimeFaces forum. A partial [solution](http://forum.primefaces.org/viewtopic.php?f=3&t=34312#p109966) was posted there but it is a hard time to update code in a jar (I don't know how to pack/unpack and change the code in a jar file) and even doing so could work either - it may or may not. – Tiny Oct 16 '13 at 22:22
  • I may be wrong but the suggestion in the Primefaces Forum is to suppress the thrown exceptions. I do not think that this will make the cause of the error magically disappear. I'll start digging in the code today. If i am able to solve the issue i'll post the solution. – Kai Oct 17 '13 at 06:34
  • This indeed worked in the given environment as mentioned in the question - files can be uploaded with AJAX submit. Thank you very much. – Tiny Oct 20 '13 at 09:50
  • It is not necessary to know though may I know what this code makes a difference to this original code in the library so that it can work? (How does it enable PrimeFaces/JSF to upload files with AJAX submit while the original code in the library fails to do the same?) Thanks again. – Tiny Oct 20 '13 at 23:51
  • I edited my answer. Hopefully it will become a little more clear why it works ;) – Kai Oct 21 '13 at 05:26
  • Thank you very much. I somehow managed to understand it (though not essentially needed). – Tiny Oct 21 '13 at 17:18
9

Although this is old and already answered, i wanted to share something, just in case you missed it: PrimeFaces 4+ now has a context paramter, which you can use (in web.xml) to manually choose which uploader should be used (native-servlet3 or commons). You can use this to force commons-uploader like this:

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

(Of course you still need FileUploadFilter as described above and in the guide). See PrimeFaces User's Guide for more information.

Mario B
  • 1,737
  • 2
  • 20
  • 37
  • Thanks Matrium. That helped. – robson Mar 27 '14 at 17:01
  • With JSF 2.2.6, when I defined this `` with the `commons` uploader in `web.xml`, the file upload listener in the associated managed bean itself was not invoked. With `auto` and `native` uploaders, the listener is invoked but the `uploadedFile.getContents()` method always returns `null`. I have to stick at `org.apache.commons.io.IOUtils.toByteArray(uploadedFile.getInputstream())` as it was previously to get the uploaded file contents. Am I still missing something else? – Tiny Mar 28 '14 at 19:29
  • @Tiny, I'm having the same trouble, when using the context-param my handler is not called, did you find a solution? – Christian Götz Apr 02 '14 at 20:06
  • @ChristianGötz : Sorrowfully not yet. I'm still fumbling for this solution. Currently, I'm getting a byte array from `InputStream` using `org.apache.commons.io.IOUtils.toByteArray(uploadedFile.getInputstream())` I will comment here, if I succeed. Thank you! – Tiny Apr 02 '14 at 20:11
2

@BalusC - Your suggestion for extending the existing FileUploadRenderer is very clean. Thanks!

Depending on the version of JSF you'r using you may see a random iFrame pop up. This is a bug noted here: JAVASERVERFACES-2843

In my first attempt to fix this (without having to upgrade to 2.2.1) I just hid the iFrame with CSS.

#JSFFrameId {
  visibility:hidden;
}

This worked but for some reason additional AJAX submits would not fire. I then called a little script to remove the iFrame and that fixed the problem.

<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();
}
Tiny
  • 24,933
  • 92
  • 299
  • 571
CodeMonkey
  • 150
  • 8
0

I had the same issue, In my case i have used primefaces file uploader in a data table, tried to modify the existing image using onRowEdit, which was end up with the same error mentioned above. Then i changed the primefaces jar to 5.1 version. Now its working fine.