JSF 2.0 Fileupload
In this post I'll show you how to create a simple JSF component to upload files to the server. Normally I would use Richfaces 4 or Primefaces (which have better components and is easier to use). However, I had many problems using this components, So I decided to implement my own component
My development enviroment is something like that:
- JSDK 6
- Maven 3
- Eclipse Helios + (plugin de maven)
- JBoss AS 6
- Seam 3
- Apache commons fileupload
IMPORTANT: I'm going to asume that you have a previous knowledge developing JEE5 applications
Define a Filter to get all the Multipart requests (based on the book click="this.target='_blank'">Core Java Server Faces).
package com.carperea.web.jsf;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
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.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
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;
@WebFilter(filterName = "FileUploadFilter", urlPatterns = "/*")
public class FileUploadFilter implements Filter {
private static final String SIZE_THRESHOLD_PARAM = "com.carperea.web.upload.sizeThreshold";
private static final String TEMP_DIRECTORY_PARAM = "com.carperea.web.upload.tempDirectory";
private int sizeThreshold = 1024;
private String tempDirectory;
@Override
public void init(FilterConfig config) throws ServletException {
tempDirectory = config.getServletContext().getInitParameter(TEMP_DIRECTORY_PARAM);
try {
String paramValue = config.getServletContext().getInitParameter(SIZE_THRESHOLD_PARAM);
if (paramValue != null) {
sizeThreshold = Integer.parseInt(paramValue);
}
} catch (NumberFormatException e) {
ServletException servletEx = new ServletException();
servletEx.initCause(e);
throw servletEx;
}
}
@SuppressWarnings("rawtypes")
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
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;
}
// Create a factory for disk-based file items
DiskFileItemFactory factory = new DiskFileItemFactory();
// Set factory constraints
factory.setSizeThreshold(sizeThreshold);
if (tempDirectory != null && !"".equalsIgnoreCase(tempDirectory)) {
factory.setRepository(new File(tempDirectory));
}
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload(factory);
try {
List items = upload.parseRequest(httpRequest);
final Map<String, String[]> map = new HashMap<String, String[]>();
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
String str = item.getString();
if (item.isFormField()) {
map.put(item.getFieldName(), new String[] { str });
} else {
httpRequest.setAttribute(item.getFieldName(), item);
}
}
chain.doFilter(new HttpServletRequestWrapper(httpRequest) {
@SuppressWarnings("unchecked")
public Map getParameterMap() {
return map;
}
public String[] getParameterValues(String name) {
Map map = getParameterMap();
return (String[]) map.get(name);
}
public String getParameter(String name) {
String[] params = getParameterValues(name);
if (params == null)
return null;
return params[0];
}
@SuppressWarnings("unchecked")
public Enumeration getParameterNames() {
Map map = getParameterMap();
return Collections.enumeration(map.keySet());
}
}, response);
} catch (FileUploadException e) {
ServletException servletEx = new ServletException();
servletEx.initCause(e);
throw servletEx;
}
}
@Override
public void destroy() {
}
}
Define the Renderer
package com.carperea.web.jsf; import java.io.IOException; import javax.el.Valueexpression; import javax.faces.component.UIComponent; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import javax.faces.render.FacesRenderer; import javax.faces.render.Renderer; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileItem; @FacesRenderer(componentFamily = FileUpload.COMPONENT_FAMILY, rendererType = FileUpload.RENDERER_TYPE) public class FileUploadRenderer extends Renderer { @Override public void encodeBegin(FacesContext context, UIComponent component) throws IOException { if (!component.isRendered()) { return; } ResponseWriter writer = context.getResponseWriter(); String clientId = component.getClientId(context); writer.startElement("input", component); writer.writeAttribute("type", "file", "type"); writer.writeAttribute("name", clientId, "clientId"); String styleClass = (String) component.getAttributes().get("styleClass"); if (styleClass != null) { writer.writeAttribute("class", styleClass, "styleClass"); } writer.endElement("input"); writer.flush(); } @SuppressWarnings("rawtypes") @Override public void decode(FacesContext context, UIComponent component) { FileUpload fileUpload = (FileUpload) component; ExternalContext external = context.getExternalContext(); HttpServletRequest request = (HttpServletRequest) external.getRequest(); String clientId = fileUpload.getClientId(context); FileItem item = (FileItem) request.getAttribute(clientId); Valueex pression ve = fileUpload.getValueex pression("value"); if (ve != null) { Class type = ve.getType(context.getELContext()); if (type == FileItem.class) { fileUpload.setSubmittedValue(item); } } } }
Modify the web.xml file to add some configuration paramaters
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 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-app_3_0.xsd">
...
<!-- file upload parameters -->
<context-param>
<param-name>com.carperea.web.upload.sizeThreshold</param-name>
<param-value>10000</param-value>
</context-param>
<context-param>
<param-name>com.carperea.web.upload.tempDirectory</param-name>
<param-value>/tmp/imgs</param-value>
</context-param>
...
</web-app>
define the component that represents the input of type file (<input type="file"/>)
package com.carperea.web.jsf; import java.util.ArrayList; import java.util.List; import javax.el.Valueexpression; import javax.faces.application.FacesMessage; import javax.faces.component.FacesComponent; import javax.faces.component.UIInput; import javax.faces.context.FacesContext; import org.apache.commons.fileupload.FileItem; @FacesComponent(value = "HtmlInputFile") public class FileUpload extends UIInput { public static final String COMPONENT_TYPE = "com.carperea.component.FileUpload"; public static final String COMPONENT_FAMILY = "com.carperea.component"; public static final String RENDERER_TYPE = "com.carperea.component.FileUploadRenderer"; private static final String OPTIMIZED_PACKAGE = "com.carperea.component."; ... private static final String ATTRIBUTES_THAT_ARE_SET = "javax.faces.component.UIComponentBase.attributesThatAreSet"; protected enum PropertyKeys { allowTypes, sizeLimit, style, styleClass; String toString; PropertyKeys() { } PropertyKeys(String toString) { this.toString = toString; } public String toString() { return ((this.toString != null) ? this.toString : super.toString()); } } public FileUpload() { setRendererType(RENDERER_TYPE); } @Override public String getFamily() { return COMPONENT_FAMILY; } public String getAllowTypes() { return (String) getStateHelper().eval(PropertyKeys.allowTypes, null); } public void setAllowTypes(String allowTypes) { getStateHelper().put(PropertyKeys.allowTypes, allowTypes); handleAttribute("allowTypes", allowTypes); } public Long getSizeLimit() { return (Long) getStateHelper().eval(PropertyKeys.sizeLimit, Long.MAX_VALUE); } public void setSizeLimit(Long sizeLimit) { getStateHelper().put(PropertyKeys.sizeLimit, sizeLimit); handleAttribute("sizeLimit", sizeLimit); } public String getStyle() { return (String) getStateHelper().eval(PropertyKeys.style, null); } public void setStyle(String style) { getStateHelper().put(PropertyKeys.style, style); handleAttribute("style", style); } public String getStyleClass() { return (String) getStateHelper().eval(PropertyKeys.styleClass, null); } public void setStyleClass(String styleClass) { getStateHelper().put(PropertyKeys.styleClass, styleClass); handleAttribute("styleClass", styleClass); } @SuppressWarnings("unchecked") private void handleAttribute(String name, Object value) { List<String> setAttributes = (List<String>) this.getAttributes().get( ATTRIBUTES_THAT_ARE_SET); if (setAttributes == null) { String cname = this.getClass().getName(); if (cname != null && cname.startsWith(OPTIMIZED_PACKAGE)) { setAttributes = new ArrayList<String>(6); this.getAttributes().put(ATTRIBUTES_THAT_ARE_SET, setAttributes); } } if (setAttributes != null) { if (value == null) { Valueex pression ve = getValueex pression(name); if (ve == null) { setAttributes.remove(name); } } else if (!setAttributes.contains(name)) { setAttributes.add(name); } } } @Override protected void validateValue(FacesContext context, Object newValue) { super.validateValue(context, newValue); if (isValid()) { if (newValue instanceof FileItem) { FileItem tmp = (FileItem) newValue; String allowTypes = getAllowTypes(); String fileName = tmp.getName(); if (allowTypes != null && !"".equalsIgnoreCase(allowTypes) && fileName != null && !"".equalsIgnoreCase(allowTypes)) { String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1); String[] exts = allowTypes.split(","); boolean validExt = false; for (String ext : exts) { validExt = validExt || ext.equalsIgnoreCase(fileExt); } if (validExt) { setValid(true); } else { setValid(false); FacesMessage message = MessageFactory.getMessage(INVALID_FILE_EXTENSION, FacesMessage.SEVERITY_ERROR); context.addMessage(getClientId(context), message); } } Long sizeLimit = getSizeLimit(); if (sizeLimit != null) { if (tmp.getSize() <= sizeLimit) { setValid(isValid() && true); } else { setValid(false); FacesMessage message = MessageFactory.getMessage(FILE_SIZE_EXCEEDED, FacesMessage.SEVERITY_ERROR); context.addMessage(getClientId(context), message); } } } else { setValid(false); FacesMessage message = MessageFactory.getMessage(DEFAULT_ERR, FacesMessage.SEVERITY_ERROR); context.addMessage(getClientId(context), message); } } } }
Create the taglib file with the definition
<?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://carperea.com/jsf/html</namespace>
<tag>
<tag-name>inputFile</tag-name>
<component>
<component-type>HtmlInputFile</component-type>
</component>
<attribute>
<description></description>
<name>id</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description></description>
<name>rendered</name>
<required>false</required>
<type>java.lang.Boolean</type>
</attribute>
<attribute>
<description></description>
<name>binding</name>
<required>false</required>
<type>javax.faces.component.UIComponent</type>
</attribute>
<attribute>
<description></description>
<name>value</name>
<required>false</required>
<type>java.lang.Object</type>
</attribute>
<attribute>
<description></description>
<name>allowTypes</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description></description>
<name>sizeLimit</name>
<required>false</required>
<type>java.lang.Long</type>
</attribute>
<attribute>
<description></description>
<name>style</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
<attribute>
<description></description>
<name>styleClass</name>
<required>false</required>
<type>java.lang.String</type>
</attribute>
</tag>
</facelet-taglib>
Modify the web.xml file to add the TAG definition
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" 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-app_3_0.xsd"> ... <context-param> <param-name>javax.faces.FACELETS_LIBRARIES</param-name> <param-value>/WEB-INF/file-taglib.xml</param-value> </context-param> ... </web-app>
Create a form with the defined component in a .xhtml file
<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:cp="http://carperea.com/jsf/html"
template="/WEB-INF/templates/default.xhtml">
...
<ui:define name="content">
<h:form enctype="multipart/form-data" >
<cp:inputFile id="image" value="#{userProfile.fileItem}"
allowTypes="png,gif,jpg" sizeLimit="716800"/>
<h:commandButton action="#{userProfile.loadImage}" value="Upload" />
</h:form>
</ui:define>
...
</ui:composition>
finally, define a managed bean to get the loaded file
package com.carperea.project.web;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.fileupload.FileItem;
import com.carperea.project.core.UserRepository;
import com.carperea.project.model.User;
@Named
@RequestScoped
public class UserProfile {
...
private FileItem fileItem;
public void loadImage() {
String mimeType = fileItem.getContentType();
String fileName = fileItem.getName();
try {
InputStream fileContenteAsInputStream = fileItem.getInputStream();
} catch (IOException e) {
...
}
byte[] fileContentAsByteArray = fileItem.get();
}
public FileItem getFileItem() {
return fileItem;
}
public void setFileItem(FileItem fileItem) {
this.fileItem = fileItem;
}
...
}
I hope this is useful for some of you...

This work by carperea is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.