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;
    }
}

Saturday, March 20, 2010

Java Servlet (3.0): How can I access multipart encoded content provided by a http post request?

At the last friday i stood right before a tricky problem: How can I save a image provided via HTTP post into a database?
I had three problems:
1. How to upload a file via HTML?
2. How to access the data at the server side?
3. How to put in the database using JPA?

The first one was easy, just create a html form add a input field (type='file') and a submit button.

The second one cost me one day. And it was really simple: Just place the @MultipartConfig annotation at the class definition of the servlet and use HTTPRequest.getPart[s]() methods to access the data as an inputstream.

The last part was straight forward: use a InputStreamReader to copy the data into a byte[] and add @Lob byte[] field to the entity class.

Because I use MySQL it was necessary to change the columnt type from TEXT to MEDIUMBLOB.