File Uploads
Adding a File Upload to a Form
Stripes makes it very easy to manage file uploads. At its simplest you just include a file field in a stripes form, and match it with a FileBean property on your ActionBean. For example:
<stripes:form> ... <stripes:file name="newAttachment"/> ... </stripes:form>
private FileBean newAttachment; public FileBean getNewAttachment() { return newAttachment; } public void setNewAttachment(FileBean newAttachment) { this.newAttachment = newAttachment; }
At this point, it would be worth going and taking a look at the JavaDoc for FileBean.
Processing Uploaded Files
Unfortunately, we're not quite done yet. Due to limitations in the HTTP specification, the way file uploading is handled may not be quite as simple as you might at first think. It is worth taking the time to understand this. While the HTTP specification defines the multipart/form-data MIME type to allow the uploading of one or more files at the same time as other request data, it does not specify that the parts of the request must come in any specific order. This means that the browser can choose to send the files for upload before any of the other request parameters (read: information in your form).
What this means is that uploaded files cannot be streamed directly to your ActionBean, because you may not be able to access other request information until after the file is uploaded. To get around this Stripes, like other implementations, streams the files to a disk, processes the request and then provides you with access to the files on the local disk. This has repercussions for how you handle the FileBean objects that get attached to your ActionBean. Generally you're going to want save the uploaded file somewhere, or read it in as a stream and do something else with it...
Saving an uploaded file
Saving the uploaded file to a specific location can be achieved simply by calling the FileBean.save(File toFile) method. All this method does is move the temporary file saved during the upload processing to the location specified by the File you provide. If you invoke this method, since the temporary file gets moved, there is no need (in fact there is a need not) to call FileBean.delete().
Reading the uploaded file as a Stream
The alternative is to process the uploaded file as an InputStream. You might choose to do this if you want to stream the contents of the file to a database, or analyze the data that is uploaded. You can do this by calling FileBean.getInputStream() and then reading the data as you would with any other InputStream. Once you are done you should call FileBean.delete() to delete the temporary file. If you don't do this then your web application's temporary directory will fill up with all the uploaded files!
Know when to delete FileBeans
If you process a FileBean
as an InputStream
, call FileBean.delete()
. If you use the save()
method, don't call FileBean.delete()
!
Multiple File Uploads Using Indexed Properties
Sometimes it will be necessary to support the uploading of more than one file where you (the developer) doesn't know the number ahead of time. For non-file fields you might do this just by including multiple copies of the field all with the same name and binding that to a List or Array in your ActionBean. Stripes doesn't support this with FileBeans though due to a limitation in one of the Multipart libraries support. It is, however, easy to mimic this behaviour using Stripes' indexed property support which works the same for FileBeans as it does for other inputs.
The following code shows how you might refactor the code from above to accept multiple files:
<stripes:form> <c:forEach ... varStatus="loop"> ... <stripes:file name="newAttachments[${loop.index}]"/> ... </stripes:form>
private List<FileBean> newAttachments; public List<FileBean> getNewAttachments() { return this.newAttachments; } public void setNewAttachment(List<FileBean> newAttachments) { this.newAttachments = newAttachments; }
Limiting the size of uploads
Unfortunately the HTTP specification gets in the way again here. The only piece of useful information available before the contents of the POST is the total size of the POST. This includes all uploaded files, all other fields in the form, any headers etc. As a result, the only thing that we can use to limit uploads is the POST size (duh!). You'll want to consider a limit carefully - it should be low enough to prevent denial of service attack, but high enough to let your users upload the kinds of files you want them to.
By default Stripes set this limit to 10 megabytes. For information on how to change the limit refer to the File Upload section in the Configuration Reference.
Alternative Implementations
The parsing of multipart form data is quite difficult to do correctly, and for this reason Stripes delegates this responsibility to well established third party libraries. The code which performs the parsing is wrapped behind an interface called MultipartWrapper. Stripes provides two implementations of MultipartWrapper
.
Why two? Earlier version of Stripes shipped with a single (non-pluggable) implementation that used the COS package. This was done for two major reasons: it has no dependencies (i.e. no other jars that must be used with it) and it provided a more intuitive programming model (for me, developing Stripes). However, the licensing for COS is less than ideal. While it will suit most commercial developers it excludes the possibility of redistributing your application without negotiating a commercial license for COS!
The result is that the second implementation now uses the Apache Commons File Upload which is covered under the much more permissive Apache License (the same license used by Stripes). The only real downside is that the commons implementation has a dependency on the commons-io package.
For backwards compatibility reasons the COS implementation has been refactored into an implementation of MultipartWrapper
called (surprisingly) CosMultipartWrapper, which continues to be the default implementation.
In case you're going to use CommonsFileupload and also have the cos library in your classpath, you can explicitly configure the CommonsMultipartWrapper by adding the following initialization parameter to the Stripes Filter:
<init-param> <param-name>MultipartWrapper.Class</param-name> <param-value>net.sourceforge.stripes.controller.multipart.CommonsMultipartWrapper</param-value> </init-param>
It is also possible to plug-in replace the code that is responsible for manufacturing an instance of MultipartWrapper in order to add additional control behaviour. This can be done by implementing MultipartWrapperFactory and specifying your custom factory as an initialization parameter to the Stripes Filter.
<init-param> <param-name>MultipartWrapperFactory.Class</param-name> <param-value>com.myco.CustomMultipartWrapperFactory</param-value> </init-param>