Search This Blog

Thursday, 29 November 2012

Upload and Download files

We often come across scenarios wherein we need to upload a file and save it on some persistent storage. We also then have to provide code for downloading the file - this involves processing the request, reading the file - from database or file system and writing it back to the response.
I decided to write a simple controller to upload and download a file. The first step was to create a JSP to upload the file:
<html>
<body>
    <form action="/SpringMVC-Annotated/file/upload.do"
        enctype="multipart/form-data" method="post">
        <p>
            Please specify a file:<br> 
        <input type="file" name="image" size="40">
        </p>
        <div>
            <input type="submit" value="Send">
        </div>
    </form>
</body>
</html>
The file includes a simple form that is used to upload a file to the server. The form type is multipart/form-data which indicates a form capable of sending simple data as well as files.
The enctype attribute indicates the encoding used. Its default value is application/x-www-form-urlencoded which is what we use in our simple forms.
This explanation is copied as is from the RFC document:
"multipart/form-data" contains a series of parts. Each part is expected to contain
a content-disposition header [RFC 2183] where the disposition type is "form-data",
and where the disposition contains an (additional) parameter of "name", where the
value of that parameter is the original field name in the form.
Each part has an optional "Content-Type", which defaults to text/plain.  If the
contents of a file are returned via filling out a form, then the file input is
identified as the appropriate media type, if known, or "application/octet-stream".
If multiple files are to be returned as the result of a single form entry, they
should be represented as a "multipart/mixed" part embedded within the
"multipart/form-data".
Each part may be encoded and the "content-transfer-encoding" header supplied if
the value of that part does not conform to the default encoding.

(That was a bit off-topic but I liked the explanation.)
To get back to our example I had to now define a controller to process the input file.
@Controller
@RequestMapping(value = "/file/")
public class FileController {

    @Autowired
    private ImageManager imageManager;

    @RequestMapping(value = "upload.do", method = RequestMethod.POST)
    public String uploadUserImage(
            @RequestParam(value = "image") final MultipartFile userImage)
            throws Exception {
        System.out.println("request received to upload image " + userImage);
        if (!userImage.isEmpty()) {
            final InputStream inputStream = new ByteArrayInputStream(
                    userImage.getBytes());
            System.out.println("uploading image wuth stream" + inputStream);
            this.imageManager.saveImage(inputStream);
            return "success";
        } else {
            throw new Exception("File not found ");
        }
    }
}
As can be seen the above code receives a MultipartFile object that represents the file received. The controller converts it to a ByteArrayInputStream and delegates the save to a file manager component. The Multipart interface is
A representation of an uploaded file received in a multipart request.
The file contents are either stored in memory or temporarily on disk. In either case, the user is responsible for copying file contents to a session-level or persistent store as and if desired. The temporary storages will be cleared at the end of request processing.
If we were to run the code :
SEVERE: Servlet.service() for servlet [springDispatcher] in context with path 
[/SpringMVC-Annotated] threw exception [Request processing failed; nested 
exception is java.lang.IllegalArgumentException: Expected 
MultipartHttpServletRequest: is a MultipartResolver configured?] with root cause
java.lang.IllegalArgumentException: Expected MultipartHttpServletRequest: 
is a MultipartResolver configured?
    at org.springframework.util.Assert.notNull(Assert.java:112)
    at org.springframework.web.method.annotation.
RequestParamMethodArgumentResolver.resolveName(RequestParamMethodArgumentResolver.java:150)
What went wrong ??
We need some library that is capable of understanding the data sent by client and converting it into the format understood by the server. More specifically a library that is capable of helping Spring achieve the MultipartFile interface.
For this add the commons-fileupload-1.2.2.jar and also initialise the below bean:
<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
     <!-- one of the properties available; the maximum file size in bytes -->
    <property name="maxUploadSize" value="100000"/>
</bean>
Each request will be inspected to see if it contains a multipart. If no multipart is found, the request will continue as expected. However, if a multipart is found in the request, the MultipartResolver that has been declared in your context will be used. After that, the multipart attribute in your request will be treated like any other attribute.
Now if we were to run the code it will work just fine. In the next post we shall look at some improvements to this code and also the download part.

1 comment: