Pages

Monday, January 11, 2010

Multipart Requests and JSF

Multipart requests are used when client needs to upload files to server. A multipart request has a different encoding and requires to be parsed.
JSF2 doesn't come with any fileupload component nor it has any support with multipart requests. A multipart request to JSF (mojarra impl. 2.0.2) just executes the 'restore view' and 'render response' phases, simply because JSF can't extract the request parameters from the multipart request.
Here is what I did to implement a file upload scenerio in JSF:
Define the view
I defined my view a form with multipart encoding and file input:
1:      <h:form enctype="multipart/form-data">
2: User : <h:inputText value="#{join.userName}"/>
3: Wall : <h:inputText value="#{join.wallName}"/>
4: Avatar : <input id="avatar" name="avatar" type="file" />
5: <h:commandButton value="Login" action="#{join.create}"/>
6: </h:form>
Wrap & Parse The Request
I used commons-fileupload to parse the request and used CDI event dispatching to assign the uploaded file to beans :
1:  public class MultipartRequestWrapper extends HttpServletRequestWrapper {
2:
3: private Hashtable<String, String[]> params = new Hashtable<String, String[]>();
4:
5: MultipartRequestWrapper(HttpServletRequest request, Event<FileItem> uploadEvents) {
6: super(request);
7: DiskFileItemFactory factory = new DiskFileItemFactory();
8: factory.setSizeThreshold(2097152);
9: ServletFileUpload upload = new ServletFileUpload(factory);
10: upload.setSizeMax(2097152);
11:
12: try {
13: List<FileItem> items = upload.parseRequest(request);
14: for (FileItem item : items) {
15: if (item.isFormField()) {
16: params.put(item.getFieldName(), new String[]{new String(item.get())});
17: } else {
18: uploadEvents.fire(item);
19: }
20: }
21: } catch (FileUploadException e) {
22: throw new RuntimeException(e);
23: }
24: }
25:
26: @Override
27: public String getParameter(String name) {
28: String [] values = getParameterValues(name);
29: if(values == null || values.length == 0) {
30: return null;
31: }
32:
33: return values[0];
34: }
35:
36: @Override
37: public Map<String, String[]> getParameterMap() {
38: return params;
39: }
40:
41: @Override
42: public Enumeration<String> getParameterNames() {
43: return params.keys();
44: }
45:
46: @Override
47: public String[] getParameterValues(String name) {
48: return params.get(name);
49: }
50:
51: }
Line 16 parses and extracts the request parameters and and line 8 publishes the file item to the listening beans. A simple filter checks the request and if it's a multipart request it wraps the request with this wrapper.