Using file upload in Liferay JSF portlet.

By Alexander Oleynik, On 6/9/09 2:41 PM

Problem description

It is not possible to use upload file control when using JSF 1.2 RI + Sun JSF portlet (Liferay Portal 5.1.2)

When I changed form enctype to "multipart/form-data" my jsf action stopped to run. The reason is in the fact that when form enctype is "multipart/form-data" bridge doesn't parse request and not filling parameters map.

Solution

Create custom JSF portlet and wrap "multipart/form-data" ActionRequest into such with parsed parameters with added upload file controls as a request attributes.

So having JSF:

<h:form enctype="multipart/form-data">
<input type="file" name="resumeFile" />
<h:commandbutton value="Apply" action="#{newJobsView.submitApply}">
</h:commandbutton>
</h:form>

In JSF action you can get attached file as:

public void submitApply() {
FacesContext facesContext = FacesContext.getCurrentInstance();

// JSFPortletUtil - Liferay provided class from util-bridges.jar
ActionRequest request = JSFPortletUtil.getPortletRequest(facesContext);

// FileItem is from commons-fileupload.jar
FileItem item = (FileItem)request.getAttribute("resumeFile");
...

}

First my File upload util class:

package com.oleynik.util;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.portlet.ActionRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.portlet.PortletFileUpload;

public class FileUploadUtil {

public static boolean isMultipart(final ActionRequest request) {
return PortletFileUpload.isMultipartContent(request);
}

public static List<FileItem> parseRequest(final ActionRequest request)
throws FileUploadException {
FileItemFactory factory = new DiskFileItemFactory();
PortletFileUpload upload = new PortletFileUpload(factory);
return upload.parseRequest(request);
}

public static Map<String, String> getParameters(final List<FileItem> items) {
Map<String, String> result = new HashMap<String, String>();
for (FileItem item : items) {
if (item.isFormField()) {
result.put(item.getFieldName(), item.getString());
}
}
return result;
}

public static Map<String, FileItem> getFiles(final List<FileItem> items) {
Map<String, FileItem> result = new HashMap<String, FileItem>();
for (FileItem item : items) {
if (!item.isFormField()) {
result.put(item.getName(), item);
}
}
return result;
}
}

Second is Wrapper ActionRequest class:

package com.oleynik.faces;

import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.filter.ActionRequestWrapper;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;

import com.oleynik.util.FileUploadUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;

public class FileUploadActionRequestWrapper extends ActionRequestWrapper {

private static final Log logger = LogFactoryUtil.getLog(
FileUploadActionRequestWrapper.class);

private Map<String, String[]> parameters;

public FileUploadActionRequestWrapper(final ActionRequest request)
throws FileUploadException {
super(request);
parameters = new HashMap<String, String[]>();
if (FileUploadUtil.isMultipart(request)) {
//logger.info("multipart");
List<FileItem> items = FileUploadUtil.parseRequest(request);
Map<String, FileItem> files = FileUploadUtil.getFiles(items);
for (String name : files.keySet()) {
setAttribute(files.get(name).getFieldName(), files.get(name));
//logger.info("file " + files.get(name).getFieldName());
}
Map<String, String> pars = FileUploadUtil.getParameters(items);
for (String name : pars.keySet()) {
String[] values = new String[1];
values[0] = pars.get(name);
parameters.put(name, values);
}
}
}

@Override
public Map<String, String[]> getParameterMap() {
return parameters;
}

@Override
public String getParameter(final String name) {
if (parameters.get(name) != null) {
return parameters.get(name)[0];
}
return null;
}

@Override
public String[] getParameterValues(final String name) {
return parameters.get(name);
}

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

And finally custom JSF portlet class:

package com.oleynik.faces;

import java.io.IOException;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;

import org.apache.commons.fileupload.FileUploadException;

import com.oleynik.util.FileUploadUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;

public class FacesPortlet extends com.sun.faces.portlet.FacesPortlet {

private static final Log logger = LogFactoryUtil.getLog(FacesPortlet.class);

public FacesPortlet() {
super();
//logger.info("init");
}

@Override
public void processAction(ActionRequest request, ActionResponse response)
throws IOException, PortletException {

ActionRequest myRequest = request;

if (FileUploadUtil.isMultipart(request)) {
//logger.info("multipart!");
try {
myRequest = new FileUploadActionRequestWrapper(request);
} catch (FileUploadException e) {
logger.error(e.getMessage());
}
}
super.processAction(myRequest, response);
}
}

Set custom JSF portlet in your portlet.xml file

<?xml version="1.0"?>
<portlet-app
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">

<portlet>
<portlet-name>1</portlet-name>
<display-name>JSF upload example</display-name>
<portlet-class>com.oleynik.faces.FacesPortlet</portlet-class>
<init-param>
<name>com.sun.faces.portlet.INIT_VIEW</name>
<value>/view.xhtml</value>
</init-param>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>JSF upload example</title>
<short-title>JSF upload example</short-title>
<keywords>JSF upload example</keywords>
</portlet-info>
<security-role-ref>
<role-name>administrator</role-name>
</security-role-ref>
<security-role-ref>
<role-name>guest</role-name>
</security-role-ref>
<security-role-ref>
<role-name>power-user</role-name>
</security-role-ref>
<security-role-ref>
<role-name>user</role-name>
</security-role-ref>
</portlet>
</portlet-app>

Note: this implementation uses commons-fileupload.jar and commons-io.jar. Created example portlet with sources is available for download.

Show comments 16