Brain Flush

December 4, 2008

How To Gracefully Recover From File Upload Errors In Grails

Filed under: Software Development & Programming — Tags: , , — Matthias @ 4:45 pm

Problem statement

It is often desirable to let users upload files to your website, such as custom profile images. This is not a trivial task however, since it is prone to errors (e.g. file sizes exceeding maximum values, or certain file types not being allowed by the system).

Grails supports file uploads through its Spring layer, which in turn uses the Apache Commons FileUpload library, a Java based library that brings support for handling HTTP requests of the multipart/form-data MIME type (that’s the format of requests submitted from an HTML form). However, there is one major problem: Any exceptions that occur while uploading files (e.g. due to oversize files) are thrown in a Servlet specific to that library, and the request never reaches any of your Grails controllers. This means that if the user tries to upload a file that is too large, the backend will bail out with an internal exception and the container will display a 500 to the user.

That’s obviously not what we want. Consider for example a signup page where the user can upload a custom profile picture. If the maximum allowed size for that picture is, say, 1MB, we don’t want the user to face a 500, but instead gracefully handle that error and set an error flag on the signup data (e.g. using Spring’s Errors.reject()).

Solution

Grails manages a Spring bean for handling file uploads called ‘multipartResolver’, which is an instance of Spring’s CommonsMultipartResolver. We can set e.g. the maximum allowed size for uploaded files on that bean using the maxUploadSize property (you could do that e.g. in BootStrap.groovy), but that alone does not solve the problem mentioned above. What we want to do is subclass CommonsMultipartResolver, handle any exceptions we do not want to reach the user, and redirect to our own error handling logic. In the case for handling exceeding file sizes, our custom resolver could look like this:

public class CustomMultipartResolver extends CommonsMultipartResolver {

    static final String FILE_SIZE_EXCEEDED_ERROR = "fileSizeExceeded"

    public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) {

        try {
            return super.resolveMultipart(request)
        } catch (MaxUploadSizeExceededException e) {
            request.setAttribute(FILE_SIZE_EXCEEDED_ERROR, true)
            return new DefaultMultipartHttpServletRequest(request, [:], [:])
        }
    }
}

In the controller responsible for processing the request, we can then do as follows:


def handleRequest = {
    def user = new User(params)
    if (request.getAttribute(CustomMultipartResolver.FILE_SIZE_EXCEEDED_ERROR)) {
        user.errors.reject("user.picture.fileSizeTooLarge")
        return [user: user]
    } else {
        // OK, continue processing
    }
}

Of course, since we replaced the implementation of Grails’ multipartResolver, we have to tell Grails we’re using a custom class. We do that via Spring in resources.groovy or resources.xml:

<beans xmlns="...">
    <bean id="multipartResolver" class="com.example.CustomMultipartResolver">
        <!-- limit uploads to 1MB -->
<property name="maxUploadSize" value="1048576" />
    </bean>
</beans>

And that’s it. If the user now uploads an image which is larger than 1MB, the website will display a validation error not visually distinguishable from other validation errors which are actually produced by Spring.

References:
http://blog.bruary.net/2008/03/grails-ajax-file-upload-progressbar.html

Create a free website or blog at WordPress.com.