Upload Files with JSF and MyFaces
The UploadedFileDefaultMemoryImpl class gets the
content of the uploaded file as well as its name, size, and content
type from a FileItem instance and stores all of this
information into private fields. Therefore, even if Commons File
Upload keeps the file on disk, this implementation of the
UploadedFile interface maintains the content of the
uploaded file in memory, wasting resources.
The UploadedFileDefaultFileImpl class uses a
transient field to keep a reference to the a FileItem
instance, which is used to obtain the content of the uploaded file
only when the getInputStream() method is called. This
implementation saves memory space, but if it's serialized, you
cannot obtain the file content after deserialization. Therefore,
the backing bean of the file-uploading form should not be kept in
the session scope, because application servers
serialize session beans when the application is restarted or when
the server shuts down.
If you want an efficient solution that works properly, keep the
backing beans in the request scope and specify
storage="file" within
<x:inputFileUpload> to save memory resources.
You could also solve the serialization problem of the
UploadedFileDefaultFileImpl class by adding a
writeObject() method that should serialize the content
of the uploaded file. To keep this implementation of
UploadedFile efficient, the corresponding
readObject() method should recreate the temporary file
instead of reading its content in memory.
There is one more thing that you should take into account when
using the MyFaces component. The UploadedFile
interface doesn't define a method for deleting the temporary files
that Commons File Upload creates on disk. These files will be
deleted only when the FileItem instances are garbage
collected. The DefaultFileItem class of Commons File
Upload has a finalize() method that deletes the
temporary file managed by the object that is removed from memory.
If your application is uploading large files, you might want to
delete them right after they are processed, without waiting for
garbage collection. To be able to do that, you would have to add a
getFileItem() method (in
UploadedFileDefaultFileImpl) that should return the
FileItem instance, which has a delete()
method.
Sample Application
The previous sections have described how MyFaces supports file
uploading with the help of Commons File Upload. Now let's see a
real application that uses this feature. A JSF form
(MyForm.jsp) lets users select a file and choose a
message-digest algorithm, which is used by a backing bean
(MyBean.java) to compute a hash value that is
displayed by another web page (MyResult.jsp). These
pages and the backing bean are glued with a JSF configuration file
(faces-config.xml).
The MyForm.jsp Page
This JSF form uses the <x:inputFileUpload>
tag of MyFaces, along with other standard JSF tags that render
labels, messages, a drop-down list that contains message-digest
algorithms, and a command button that uses a JSF expression
(#{myBean.processMyFile}) to specify the action method
that processes the uploaded file:
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://myfaces.apache.org/extensions" prefix="x"%>
<f:view>
<h:form id="MyForm" enctype="multipart/form-data" >
<h:messages globalOnly="true" styleClass="message"/>
<h:panelGrid columns="3" border="0" cellspacing="5">
<h:outputLabel for="myFileId" value="File: "/>
<x:inputFileUpload id="myFileId"
value="#{myBean.myFile}"
storage="file"
required="true"/>
<h:message for="myFileId"/>
<h:outputLabel for="myParamId" value="Param: "/>
<h:selectOneMenu id="myParamId"
value="#{myBean.myParam}"
required="true">
<f:selectItem itemLabel="" itemValue=""/>
<f:selectItem itemLabel="MD5" itemValue="MD5"/>
<f:selectItem itemLabel="SHA-1" itemValue="SHA-1"/>
<f:selectItem itemLabel="SHA-256" itemValue="SHA-256"/>
<f:selectItem itemLabel="SHA-384" itemValue="SHA-384"/>
<f:selectItem itemLabel="SHA-512" itemValue="SHA-512"/>
</h:selectOneMenu>
<h:message for="myParamId"/>
<h:outputText value=" "/>
<h:commandButton value="Submit"
action="#{myBean.processMyFile}"/>
<h:outputText value=" "/>
</h:panelGrid>
</h:form>
</f:view>