Thursday, September 16, 2010

JSF 2.0 Fileupload Component (for Servlet 2.5 and 3.0)

Introduction
Uploading of files in web application one of the core tasks. Therefore any technology or framework must implement or have a provision to enable file uploading.

However, file uploading has been a challenge when using all versions of JSF. To achieve this, most of the developers decided to develop their own component which can be integrated with JSF.

The coming of JSF 2.0 and Servers which support Servlet 3.0 has set some developers behind a thick dark cloud of indecision of either remaining with their 'old' technologies (eg Servlet 2.5) or move to new ones (eg Servlet 3.0).

It is for this purpose I revised the upload component which I posted on JSF Forum last year Decemeber. This component has a custom MultipartFilter which filters any request posted to the server and MultipartRequest which parses the multipart/form-data request into parameters and attributes. The componet was tested on tomcat 6, tomcat 7 and JBoss 6.

Requirements
You need to have any IDE of you choice eg Eclipse or Netbeans
  • JSF 2.0 libraries (jsf_api.jar and jsf_impl.jar)
  • JSTL 1.2 libraries (jstl_api.jar and jstl_impl.jar)
  • Any of the web contatainer or application server eg tomcat, glassfish, jboss etc
Creating a MultipartFilter Class
Create a Dynamic Web Project and select any runtime environment you want to choose. It can be tomcat 5, 6, 7 or JBoss 6. Create a class called MutultipartFilter in the following package com.benjmaz.faces.filter. This class is used to filter any request destined to the server. Therefore there is no need of creating a specific Servlet to handle the suppload. Certainly I did deliberately not to include @WebFilter which is present in Servlet 3.0 for compatibility with other versions fo Servlet. The configuration for the filter will have to be done manually in the web.xml(as shown later in this post). Below is the complete

package com.benjmaz.faces.filter;

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Map;
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 javax.servlet.http.HttpServletRequestWrapper;

import com.benjmaz.multipart.MultipartRequest;
import com.benjmaz.multipart.UploadedFileException;

/**
* Servlet Filter implementation class MultipartFilter
*/
public class MultipartFilter implements Filter {
 private long maxFileSize=0;
 private String repositoryPath;

 /**
 * Default constructor.
 */
 public MultipartFilter() { }
 public void init(FilterConfig config) throws ServletException {
  String maxFileSize = config.getInitParameter("maximumFileSize");
  if (maxFileSize != null) {
   if (!maxFileSize.matches("^\\d+$")) {
    throw new ServletException("MultipartFilter parameter 'maxFileSize' is not numeric!");
   }
   this.maxFileSize = Long.parseLong(maxFileSize);
  }

  repositoryPath=config.getInitParameter("repositoryPath");
 }

 /**
 * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
 */
 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
 HttpServletRequest httpRequest=(HttpServletRequest) request;
 if(isMultipart(httpRequest)) {
  try {
   //instantiate the MultipartRequest
   MultipartRequest mr;
   if(repositoryPath==null)
    mr=new MultipartRequest(request);
   else
    mr=new MultipartRequest(request,repositoryPath);

   request=MultipartFilter.wrapRequest(httpRequest, mr.getParameterMap());
   } catch (UploadedFileException e) {
    e.printStackTrace();
   }
  }
  chain.doFilter(request, response);
 }

  private static HttpServletRequest wrapRequest(HttpServletRequest request,final Map<String,String[]> parameterMap) {
   return new HttpServletRequestWrapper(request) {
   // inner methods passed as parameters
   public Map<String,String[]> getParameterMap() {
    return parameterMap;
   }

   public String[] getParameterValues(String name) {
    return (String[]) parameterMap.get(name);
   }

   public String getParameter(String name) {
    String[] params = getParameterValues(name);
    if (params == null)
     return null;
    return params[0];
   }

   public Enumeration<String> getParameterNames() {
    return Collections.enumeration(parameterMap.keySet());
   }
  };
 }

 private boolean isMultipart(HttpServletRequest request){
  if(request.getContentType()!=null)
   if(request.getContentType().toLowerCase().indexOf("multipart/")!=-1)
    return true;
  return false;
 }

 /**
 * @see Filter#destroy()
 */
 public void destroy() {
  // TODO Auto-generated method stub
 }
}

Create a MultipartRequest
Create a MultipartRequest in the following package com.benjmaz.multipart. The purpose of this class is to parse the multipart/form-data into associated parameters and attributes. It also creates file(s) in the root directory

package com.benjmaz.multipart;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;

public class MultipartRequest {
  private ServletRequest request;

  // private final String contentDisposition="Content-Disposition:";
  // private final String contentType="Content-Type";
  private final String CR = "\r";
  private final String LF = "\n";
  private final String DASH = "-";
  private Map<String, String[]> map = new HashMap<String, String[]>();
  private String dir = "";

  /**
   * 
   * @param request
   * @throws IOException
   * @throws UploadedFileException
   */
  public MultipartRequest(ServletRequest request) throws IOException,
      UploadedFileException {
    this.request = request;
    this.dir = getFilePath(dir);
    parseRequest();

  }

   /**
   * 
   * @param request
   * @param dir
   *            A directory to the root path of the web application
   * @throws IOException
   * @throws UploadedFileException
   */
  public MultipartRequest(ServletRequest request, String dir)
      throws IOException, UploadedFileException {
    this.request = request;
    this.dir = getFilePath(dir);
    parseRequest();
  }

  private void parseRequest() throws IOException, UploadedFileException {
    StringBuffer sb = new StringBuffer();
    BufferedInputStream bs = new BufferedInputStream(
        request.getInputStream(), 8 * 1024);

    int c;

    while ((c = bs.read()) != -1) {
      sb.append((char) c);
    }
    String stream = sb.toString();
    // get the boundary of the inputstream
    String boundary = getBoundery(stream);
    // remove the lass boundary in the stream which is in the form
    // --------------------7876899227363389393763--
    stream = stream.substring(0, stream.indexOf(boundary + DASH + DASH));
    // split the content by using the boundary delimeter
    String[] data = stream.split(boundary + CR + LF
        + "Content-Disposition: form-data; name=");
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    final Map<String, String[]> parameterMap = new HashMap<String, String[]>();

    for (int i = 1; i < data.length; i++) {
      String name = data[i].substring(1, data[i].indexOf("\"", 2));
      String filename = null;
      String contentType = null;
      String value = null;
      // extract the value of any given input element
      value = data[i].substring(data[i].indexOf(CR + LF + CR + LF) + 4,
          data[i].length() - 2);

      if (data[i].substring(data[i].indexOf("\"") + name.length() + 1,
          data[i].indexOf(CR + LF + CR + LF)).indexOf("filename=\"") != -1)
        filename = data[i].substring(
            data[i].indexOf("filename=\"") + 10,
            data[i].indexOf("\"" + CR + LF));

      if (data[i].indexOf("Content-Type") != -1)
        contentType = data[i].substring(data[i].indexOf(CR + LF) + 16,
            data[i].indexOf(CR + LF + CR + LF));

      putStringParameters(name, value, parameterMap);
      // if the filename is not null, then a file exists hence store the
      // uploaded file
      // to the attribute of the ServletRequest object
      if (filename != null && value.trim().length() > 0) {
        putUploadedFileParameters(httpRequest, name,
            createFile(value, filename), contentType);
      }
    }

  }

  private void putStringParameters(String name, String value,
      Map<String, String[]> parameterMap) {
    String[] values = (String[]) parameterMap.get(name);
    if (values == null) {
      parameterMap.put(name, new String[] { value });
      map.put(name, new String[] { value });
    } else {
      int length = values.length;
      String[] newValues = new String[length + 1];
      System.arraycopy(values, 0, newValues, 0, length);
      newValues[length] = value;
      parameterMap.put(name, newValues);
      map.put(name, newValues);
    }
  }

  /***
   * The method sets the value of the upload file in form of an attribute to
   * the ServletRequest object The attribute supports a single value,
   * therefore it is not possible to have multiple values in one attribute
   * This is due to the fact that JSF does not support multiple elements
   * having the same id
   * 
   * @param request
   * @param name
   * @param filename
   * @param contentType
   */
  private void putUploadedFileParameters(ServletRequest request, String name,
      String filename, String contentType) {
    request.setAttribute(name, new UploadedFile(dir, filename, contentType));
  }

  private String createFile(String content, String filename)
      throws UploadedFileException, IOException {
    
    // check if the supplied directory is a valid directory
    if (!(new File(dir)).isDirectory())
      throw new UploadedFileException("Invalid upload directory ("+dir+")");

    // check of the directory is not write protected
    if (!(new File(dir)).canWrite())
      throw new UploadedFileException("The directory ["
          + (new File(dir)).getAbsolutePath()
          + "] is either write protected or does not exist");

    // Replace all back slashes with forward slashes (filename from IE comes
    // as an absolute path from the client).
    filename = filename.replace("\\", "/").substring(
        filename.lastIndexOf('/') + 1);

    // Get filename prefix (actual name) and suffix (extension).
    String prefix = filename;
    String suffix = "";
    if (filename.contains(".")) {
      prefix = filename.substring(0, filename.lastIndexOf('.'));
      suffix = filename.substring(filename.lastIndexOf('.'));
    }

    // Write the uploaded file as the temporary file.
    File file = File.createTempFile(prefix + "_", suffix, new File(dir));

    BufferedOutputStream bos = new BufferedOutputStream(
        new FileOutputStream(file), 10 * 1024);
    for (int x = 0; x < content.length(); x++) {
      bos.write((int) content.charAt(x));
    }
    bos.close();

    return file.getName();
  }

  private String getBoundery(String stream) {
    return stream.substring(0, stream.indexOf("\r"));
  }

  public Object getParameter(String key) {
    return map.get(key);
  }

  public Map<String, String[]> getParameterMap() {
    return map;
  }

  public Enumeration<String> getParameterNames() {
    return Collections.enumeration(map.keySet());
  }

  private String getFilePath(String dir) {
    return request.getServletContext().getRealPath(dir);
  }
}

Create an UploadedFile Class
Create an uploaded file in the the following package com.benjmaz.multipart. Below is the code

package com.benjmaz.multipart;

import java.io.File;
import java.io.Serializable;

public class UploadedFile implements Serializable{
 
 private static final long serialVersionUID = 3492495123134214556L;
 private String dir;
 private String filename;
 private String type;

 public UploadedFile(String dir, String filename, String type) {
  this.dir = dir;
  this.filename = filename;
  this.type = type;
 }

 public String getContentType() {
  return type;
 }

 public String getFileName() {
  return filename;
 }
 
 public String getFilesystemName() {
  //return filename;
  return new File(dir + File.separator + filename).getAbsolutePath();
 }
 
 public File getFile() {
  if (dir == null || filename == null) {
   return null;
  } else {
   
   return new File(dir + File.separator + filename);
  }
 }
 
 public long getSize(){
  return (new File(dir + File.separator + filename)).getAbsoluteFile().length();
 }
 
 public boolean delete(){
  return (new File(dir + File.separator + filename)).delete();
 }
}

Create UploadFileException Class
Create UploadFileException class in the same package ie com.benjmaz.multipart. The code is shown below

package com.benjmaz.multipart;

public class UploadedFileException extends Exception {

 private static final long serialVersionUID = 4971742392550406967L;

 public UploadedFileException(String msg) {
  super(msg);
 }
}

Create a HtmlFileRender Class
This class is used to create a file upload tag. It also decodes the value that was passed to it. Below is the complete code and is set in the com.benjmaz.faces.renderer package

package com.benjmaz.faces;

import java.io.IOException;

import javax.el.ValueExpression;
import javax.faces.component.EditableValueHolder;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.component.behavior.ClientBehaviorHolder;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.servlet.http.HttpServletRequest;

import com.benjmaz.multipart.UploadedFile;

@FacesComponent(value = "fileUpload")
public class HtmlFileRenderer extends UIInput implements ClientBehaviorHolder {
   @Override
   public String getFamily() {
      return "fileUpload";
   }

   @Override
   public void encodeEnd(FacesContext context) throws IOException {
      ResponseWriter rw = context.getResponseWriter();
      String clientId = getClientId(context);

      rw.startElement("input", null);
      rw.writeAttribute("id", clientId, "id");
      rw.writeAttribute("name", clientId, "name");
      rw.writeAttribute("type", "file", "file");

      UIComponent component = getCurrentComponent(context);

      // add CSS Class if set
      String cssClass = (String) component.getAttributes().get("styleClass");
      if (cssClass != null)
         rw.writeAttribute("class", cssClass, "styleClass");

      // add CSS Style if set
      String style = (String) component.getAttributes().get("style");
      if (style != null)
         rw.writeAttribute("style", style, "style");

      rw.endElement("input");
      rw.flush();
   }

   @Override
   public void decode(FacesContext context) {
      ExternalContext external = context.getExternalContext();
      HttpServletRequest request = (HttpServletRequest) external.getRequest();
      String clientId = getClientId(context);
      UploadedFile file = (UploadedFile) request.getAttribute(clientId);

      ValueExpression valueExpr = getValueExpression("value");
      if (valueExpr != null & file != null) {

         UIComponent component = getCurrentComponent(context);

         if (file.getSize()  > 0) {
            ((EditableValueHolder) component).setSubmittedValue(file);
         } else {
            ((EditableValueHolder) component).setSubmittedValue(null);
         }
         ((EditableValueHolder) component).setValid(true);
      }
   }
}

Create FileValidator Class
This class will be to create a customer validor for the uploaded file. The class must be in com.benjmaz.faces.validator package
package com.benjmaz.faces.validator;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.FacesValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

import com.benjmaz.multipart.UploadedFile;

@FacesValidator(value = "fileValidator")
public class FileValidator implements Validator {
  // Restricting to 2MB
  private long maxFileSize = 0;

  @Override
  public void validate(FacesContext context, UIComponent component,
      Object value) throws ValidatorException {
    UploadedFile file = (UploadedFile) value;
    
    maxFileSize=getMaxFileSize(context);
    System.out.println(file.getSize() + "  " + maxFileSize);
    if (maxFileSize>0 && file.getSize() > maxFileSize) {
      file.delete();
      throw new ValidatorException(new FacesMessage(String.format(
          "File exceeds maximum permitted size of %d Kilo Bytes.",
          maxFileSize / 1024)));
    }
  }
  
  private long getMaxFileSize(FacesContext context){
    long initMaxFileSize=0;
    String value=context.getExternalContext().getInitParameter("maximumFileSize");
    try{
      if(value!=null)
        initMaxFileSize=Long.parseLong(value);
    }catch(Exception ex){
      throw new IllegalArgumentException("Invalid value for maxFileSize ("+value+"). It must be an integer");
    }
    return initMaxFileSize;
  }
}
Register the validator in faces-config
@FacesValidator annotation enable the registering of a validator, however I seem to have problems with JBoss therefore I also had to also include the manual settings in the faces-config.xml file. Copy and paste the following code in the faces-config
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="http://java.sun.com/xml/ns/javaee"
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-facesconfig_2_0.xsd"
version="2.0">
<validator>
<validator-id>fileValidator</validator-id>
<validator-class>com.benjmaz.faces.validator.FileValidator</validator-class>
</validator>
</faces-config>
Create A fileUpload-taglib.xml
fileUpload-taglib.xml is a configuration file which contains the namespace we will be using to embed a fileUpload component. The file can be saved in the WEB-INF folder
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee"
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-facelettaglibrary_2_0.xsd"
version="2.0">
<namespace>http://java.sun.com/jsf/fileUpload</namespace>
<tag>
<tag-name>fileUpload</tag-name>
<component>
<component-type>fileUpload</component-type>
</component>
</tag>
</facelet-taglib>
Basic Use of the component
In the above section I have highlighted how to create an upload component. Now its time to use the component
Register the Taglib file
In web.xml file register the taglib by adding the following lines
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/fileUpload-taglib.xml</param-value>
</context-param>
Register the filter
In the web.xml file add the following line
<filter>
<display-name>MultipartFilter</display-name>
<filter-name>MultipartFilter</filter-name>
<filter-class>com.benjmaz.faces.filter.MultipartFilter</filter-class>
<init-param>
<description>
Sets the path to which the uploaded files will be stored By defualt the files are .
stored in the root folder of the web application. I you want to store the files to the
different folder, you can set it here. However this is optional
</description>
<param-name>repositoryPath</param-name>
<param-value>/upload</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MultipartFilter</filter-name>
<url-pattern>*.jsf</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>MultipartFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
Complete web.xml
Complete web.xml file must look like this.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>FileUpload</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<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>
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/fileUpload-taglib.xml</param-value>
</context-param>
<filter>
<display-name>MultipartFilter</display-name>
<filter-name>MultipartFilter</filter-name>
<filter-class>com.benjmaz.faces.filter.MultipartFilter</filter-class>
<init-param>
<description>
Sets the maximum file size of the uploaded file in bytes. Set to 0 to indicate an
unlimited file size. The example value of 209152 indicates a maximum file size of
2MB ie 1024 * 1024 * 2.  This parameter is not required and can be removed safely.
</description>
<param-name>maxFileSize</param-name>
<param-value>2097152</param-value>
</init-param>
<init-param>
<description>
Sets the path to which the uploaded files will be stored By defualt the files are .
stored in the root folder of the web application. I you want to store the files to the
different folder, you can set it here. However this is optional
</description>
<param-name>repositoryPath</param-name>
<param-value>/upload</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MultipartFilter</filter-name>
<url-pattern>*.jsf</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>MultipartFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<context-param>
<description>
Sets the maximum file size of the uploaded file in bytes. Set to 0 to indicate an
unlimited file size. The example value of 209152 indicates a maximum file size of
2MB ie 1024 * 1024 * 2.  This parameter is not required and can be removed safely.
</description>
<param-name>maxFileSize</param-name>
<param-value>2097152</param-value>
</context-param>

</web-app>
Create a Backing Bean called MyBean
MyBean will be used to test the working of our fileUpload component. Below is the code. Not the file is packaged in com.benjmaz.demo.bean. (However the package is not 'necessary')
package com.benjmaz.demo.bean;

import javax.faces.bean.ManagedBean;
import com.benjmaz.multipart.UploadedFile;

@ManagedBean
public class MyBean {
   private String firstName;
   private String lastName;
   private UploadedFile file1;
   private UploadedFile file2;
   private UploadedFile file3;
   private boolean uploaded=false;

   public String doUpload(){
      uploaded=true;
      return "uploaded";
   }

   public String getFirstName() {
      return firstName;
   }

   public void setFirstName(String firstName) {
      this.firstName = firstName;
   }

   public String getLastName() {
      return lastName;
   }

   public void setLastName(String lastName) {
      this.lastName = lastName;
   }

   public UploadedFile getFile1() {
     return file1;
   }

   public void setFile1(UploadedFile file1) {
      this.file1 = file1;
   }

   public UploadedFile getFile2() {
      return file2;
   }

   public void setFile2(UploadedFile file2) {
     this.file2 = file2;
   }
  
   public UploadedFile getFile3() {
      return file3;
   }

   public void setFile3(UploadedFile file3) {
      this.file3 = file3;
   }

   public boolean getUploaded() {
     return uploaded;
   }

   public void setUploaded(boolean uploaded) {
       this.uploaded = uploaded;
   }
}
Create an JSF file (index.xhtml)
For our view we will need to create a web page. Therefore you have to create a JSF File. I have named it index.xhtml and will access it in the browser as index.jsf
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:fu="http://java.sun.com/jsf/fileUpload" xmlns:h="http://java.sun.com/jsf/html">
<head>
<meta equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Insert title here</title>
</head>
<body>

<h:form method="post" enctype="multipart/form-data">

First Name <h:inputText id="fname" value="#{myBean.firstName}" /><br />
Last Name <h:inputText id="lname" value="#{myBean.lastName}" /><br />
File 1 <fu:fileUpload id="pic1" value="#{myBean.file1}" />
<f:validator validatorId="fileValidator" />

</fu:fileUpload><h:message for="pic1" /><br />
File 2 <fu:fileUpload id="pic2" value="#{myBean.file2}" /><br />
File 3 <fu:fileUpload id="pic3" value="#{myBean.file3}" /><br />
<h:commandbutton value="Upload" action="#{myBean.doUpload}" />
<h:panelGrid columns="2" rendered="#{myBean.uploaded}">
<h:outputText value="First Name" /><h:outputText value="#{myBean.firstName}" />
<h:outputText value="Last Name" /><h:outputText value="#{myBean.lastName}" />
<h:outputText value="File 1" /><h:outputText value="#{myBean.file1.fileName}" />
<h:outputText value="File 2" /><h:outputText value="#{myBean.file2.fileName}" />
<h:outputText value="File 3" /><h:outputText value="#{myBean.file3.fileName}" />
</h:panelGrid>

</h:form>
</body>
</html>
This marks the end of task.
Note: You dont have to create a dedicated servlet to handle the fileupload. MultipartFilter and MultipartRequest will do add the needful
Thanks for having time to read all the 'junk'! I hope now you have wings to fly on high!

5 comments:

  1. Wow , thanks for this effort. i am going to adapt this and improve to ajax version with progress bar.which i could not find as a one component.

    ReplyDelete
    Replies
    1. Thanks for having time to read through. I made a few updates to some of the sections which were not formated well especially JSF (XHTML) files. I hope you wont have any problems

      Delete
  2. Hey there, thank you for this nice tutorial. I have one issue though that i'd like to ask you.
    Is there any way to change the value (text) of the Button next to the imput field?

    ReplyDelete
  3. i got this exception

    javax.faces.view.facelets.TagException: /index.xhtml @29,65 Tag Library supports namespace: http://java.sun.com/jsf/html, but no tag was defined for name: commandbutton

    at com.sun.faces.facelets.compiler.CompilationManager.pushTag(CompilationManager.java:304)

    ReplyDelete
  4. I'm also got this same exception javax.faces.view.facelets.TagException: /index.xhtml @29,65 Tag Library supports namespace: http://java.sun.com/jsf/html, but no tag was defined for name: commandbutton

    ReplyDelete