Archive for May 8th, 2008

Handling file upload with gwt-ext and spring mvc

Finally i managed to get file upload works using a gwt-ext client against spring mvc.
In GWT application file upload is something different,
you cannot do so through GWT-RPC due to the browser implementation.
File upload is only achievable through multipart form post.

On the client side you have to set up a form:


final FormPanel formPanel = new FormPanel();
// setFileUpload(true) is important because it sets the
// <form>'s content type to multipart
// so that your server knows what to do
formPanel.setFileUpload(true);

final TextField file = new TextField("File", "file");
file.setInputType("file");
formPanel.add(file);

formPanel.addButton(new Button("Upload", new ButtonListenerAdapter() {
    public void onClick(Button button, EventObject e) {
        formPanel.getForm().submit(url, null, Connection.POST, "loading...", false);
    }
}));

in the spring servlet config,
you will have to map the url you are POST-ing against to a controller that is responsible for handling file upload.


<!-- specify the java bean to bind the file upload as a byte array in the property 'commandClass" -->
<bean id="fileUploadController" class="example.server.FileUploadController">
	<property name="commandClass" value="example.web.bean.FileUploadBean"/>
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <value>
            /**/fileupload.smvc=fileUploadController
        </value>
    </property>
</bean>
<!-- this bean is how spring mvc detects multipart form post -->
<!-- commons-fileupload jar is required for this bean to work -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<property name="maxUploadSize" value="100000"/>
</bean>

finally the controller code is easy,
at first I tried to subclass SimpleFormController,
but it involves specifying a formView and successView,
which, for a GWT client you are not going to need “view”.
so I simply extend the AbstractCommandController.
It binds the request parameters of a POST request to a commandClass,
this serves my purpose nicely.


public class FileUploadController extends AbstractCommandController {

    protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object command, BindException errors) throws Exception {
        // you will need to cast the command back to your commandClass
        FileUploadBean fileUploadBean = (FileUploadBean) command;
        byte[] file = fileUploadBean.getFile();

        // do whatever logic you needed
        // because we are not going to return a "view"
        // we simply have to give out some response to the GWT client and return a null view.
        response.getWriter().println("File upload succeeded!");

        return null;
    }

    // this method is overriding, and specify how spring convert multipart into a byte array that binds to our command class
    protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
	    binder.registerCustomEditor(byte[].class, new ByteArrayMultipartFileEditor());
	    super.initBinder(request, binder);
    }
}

Integrating with spring mvc provides you free infrastructure of data binding, dependency injection on the controller,
and validation with spring’s Validator.

Hope this helps.