h: commandButton does not trigger the action if the form h: contains enctype = & ldquo; multipart / form-data & rdquo;

advertisements

JSF: 1.2

Server: Weblogic

I am trying to implement multiple file upload. As I need to give support for IE7 so I cannot use HTML5 input file. So I have planned to add a button, on clicking it will add a input file in the page.

Primarily I have started my work with ADF Faces. But that didn't worked. That was behaving in unexpected way.

I have also tried Tomahawk's file upload component, but the problem was with this component that after adding a new file upload from the backend the previously created file upload fields were get empty; not the UploadedFile instance. But this will not meet my requirement. As I need to show all the path in the file uploaders until the final Submit form button has been clicked.

Then I took help of apache commons fileupload.

I have tried this with pure JSP and apaches fileupload and that worked well.

But I want to implement it with JSF with apaches fileupload, and when I tried to do this it started causing issues.

The jspx page is given below:

<?xml version='1.0' encoding='utf-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html">
  <jsp:output omit-xml-declaration="true" doctype-root-element="HTML"
              doctype-system="http://www.w3.org/TR/html4/loose.dtd"
              doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"/>
  <jsp:directive.page contentType="text/html;charset=utf-8"/>
  <f:view>
    <html>
      <head>
        <meta http-equiv="Content-Type"
              content="text/html; charset=utf-8"/>
        <title>home</title>
      </head>
      <body>
        <h:form enctype="multipart/form-data">
            <input type="file" name="file"/>
            <h:commandButton value="Upload" action="#{uploadBean.upload}" />
        </h:form>
      </body>
    </html>
  </f:view>
</jsp:root>

I have created a filter as I cannot get the proper multipart request from the action event.

The web.xml is:

<?xml version = '1.0' encoding = 'windows-1252'?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>server</param-value>
  </context-param>
  <filter>
    <filter-name>UploadFilter</filter-name>
    <filter-class>com.edfx.massupload.filter.UploadFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>UploadFilter</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>/faces/*</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>/home.jspx</welcome-file>
  </welcome-file-list>
</web-app>

The UploadFilter:

import java.io.IOException;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

public class UploadFilter implements Filter {
    private FilterConfig _filterConfig = null;

    public void init(FilterConfig filterConfig) throws ServletException {
        _filterConfig = filterConfig;
    }

    public void destroy() {
        _filterConfig = null;
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("Filter");
        if (!(request instanceof HttpServletRequest)) {
            chain.doFilter(request, response);
            return;
        }

        HttpServletRequest httpRequest = (HttpServletRequest)request;

        boolean isMultipartContent = ServletFileUpload.isMultipartContent(httpRequest);

        if (!isMultipartContent) {
            chain.doFilter(request, response);
            return;
        }

        long maxFileSize = (1024 * 1024 * 1024);
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);
        upload.setHeaderEncoding("UTF-8");
        upload.setSizeMax(maxFileSize);
        upload.setFileSizeMax(maxFileSize);

        try {
            List<FileItem> items = upload.parseRequest(httpRequest);
            System.out.println(items.size());
            List<FileItem> files = new ArrayList<FileItem>();
            for (FileItem item : items) {
                if (!item.isFormField()) {
                    files.add(item);
                }
            }

            httpRequest.setAttribute("files", files);
        } catch (FileUploadException ex) {
            ex.printStackTrace();
        }        

        chain.doFilter(request, response);
    }
}

And lastly the managed bean:

import javax.faces.context.FacesContext;

import javax.servlet.http.HttpServletRequest;

public class UploadBean {
    public UploadBean() {
        super();
    }

    public String upload() {
        System.out.println("====JYM");
        HttpServletRequest httpRequest = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
        System.out.println(httpRequest.getAttribute("files"));
        return "";
    }
}

My aim is to add the input file field dynamically by JavaScript or jQuery, which works well in JSP, I want to get that in JSF.

If I remove the enctype="multipart/form-data" from the h:form then the action method is executing, otherwise not.

Any suggestion would be very helpful to me.


Here,

for (FileItem item : items) {
    if (!item.isFormField()) {
        files.add(item);
    }
}

you're ignoring all form fields, such as the button itself. When continuing such a request, JSF will have no clue that a button was been invoked and thus not queue any action at all.

You need to add an else to collect all form fields in a Map<String, String[]> and wrap the request with a HttpServletRequestWrapper which delegates to that map on all getParameterXxx() calls and finally continue the filter chain with the wrapped request. This way JSF will find out that the button was invoked and then queue to the proper action.