Monday, March 29, 2010

JSF 2.0: Mojarra and multipart/form-data (File Upload) [Glassfish]

It is a quite a mess that Mojarra doesn't support h:forms that use enctyp multipart/form-data, because you can't access possible available parts in the JSF Controllers.

The following wrapper extends a HttpServletRequest so that getParameter also uses available data in HttpServletRequest.getParts().

You can than access the HttpServletRequest via
FacesContext.getCurrentInstance().getExternalContext() and use getParts own your own.


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.Part;

/**
 * The mojarra implementation does not parse POST request that are multipart encoded, 
 * because the parameters are not accessable via getParameter().
 *
 * This class extends the HttpServletRequest to provide access to the parameters
 * which are encoded accessable via getParts.
 *
 * All parts are made visible that have contentType == null && size < 300.
 *
 * If the request is not multipart encoded, the wrapper doesn't modify the behavior of the original HttpServletRequest.
 * @author dennis
 */
public class MultipartHTTPServletRequest extends HttpServletRequestWrapper {

    protected Map parameterParts = new HashMap();

    public MultipartHTTPServletRequest(HttpServletRequest request) {
        super(request);

        if (getContentType() == null) {
            return;
        }
        if (!getContentType().toLowerCase().startsWith("multipart/")) {
            return;
        }
        try {
            for (Part i : getParts()) {
                if (i.getContentType() == null && i.getSize() < 300) {
                    parameterParts.put(i.getName(), getData(i.getInputStream()));
                }
            }
        } catch (IOException ex) {
            Logger.getLogger(MultipartHTTPServletRequest.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ServletException ex) {
            Logger.getLogger(MultipartHTTPServletRequest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private static String getData(InputStream input) throws IOException {
        String data = "";
        String line = "";
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        while ((line = reader.readLine()) != null) {
            data += line;
        }
        return data;
    }

    @Override
    public String getParameter(String name) {
        String result = super.getParameter(name);
        if (result == null) {
            return parameterParts.get(name);
        }
        return result;
    }
}

1 comment:

  1. To be fair, it's not Mojarra's shortcoming, but seems to be Servlet's. If you look at section 3.1.1 of the Servlet spec (2.5 or 3.0), you'll see that the encoding type must be application/x-www-form-urlencoded (among other things) for the POST data to be made available via the request parameter map. I was just bitten by this oddity as well. I'm not sure how I've missed that all these years, but somehow I did. :)

    At any rate, thanks for code above. It should help me out. :P

    ReplyDelete

Note: Only a member of this blog may post a comment.