In this recipe, you will see how to implement a JSF application for uploading files using a dedicated component developed under the Apache MyFaces Tomahawk project.
We developed this recipe with NetBeans 6.8, JSF 1.2, and GlassFish v3. The JSF 1.2 classes were obtained from the NetBeans JSF 1.2 bundled library. In addition, we have used Apache MyFaces Tomahawk 1.1.9, which provides support for JSF 1.2. You can download this distribution from http://myfaces.apache.org/tomahawk/index.html. The Apache MyFaces Tomahawk libraries (including necessary dependencies) are in the book code bundle, under the /JSF_libs/Apache Tomahawk JSF 1.2
folder.
As we said earlier, this time we will implement an upload application using Apache MyFaces Tomahawk. To be more exactly, we will use a dedicated tag, named inputFileUpload
. The tag class of this component is HtmlInputFileUploadTag
and it creates a file-selection widget in the rendered page, which allows a user to select a file for uploading to the server. The following table—extracted from the official javadoc—describes the most used attributes of this tag:
Name |
Required |
Description |
---|---|---|
|
No |
This setting was intended to allow control over how the contents of the file get temporarily stored during processing. It allows three options: "default": The file is handled in memory while the file size is below "memory": The file is loaded to memory when decode occur (set submitted value). In other words, before setting the uploaded file a submitted value it is loaded to memory. Use with caution, because it could cause |
|
No |
An EL expression to which an |
|
No |
A Boolean value that indicates whether an input value is required. |
|
No |
Identifies a backing bean property (of type |
Now, based on the description of the attributes, we have configured the following inputFileUpload
tag:
<h:panelGrid columns="3"> <h:outputLabel for="fileID" value="Choose a file to upload:" /> <t:inputFileUpload id="fileID" value="#{uploadBean.uploadedFile}" storage="file" required="true" /> <h:message showSummary="true" showDetail="false" for="fileID" style="color: red; text-decoration:overline"/> <h:panelGroup /> <h:commandButton value="Submit" action="#{uploadBean.submit}" /> <h:message for="uploadForm" infoStyle="color: blue;" errorStyle="color: red;" /> </h:panelGrid>
Now, let's focus on the value
attribute. This attribute indicates a bean that is responsible for the upload process (take a closer look on the UploadedFile
object, since this is the "brain" of our bean—it is pretty intuitive what is going on). We have implemented this bean as shown next:
package uploadpkg; import org.apache.myfaces.custom.fileupload.UploadedFile; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; public class UploadBean { private UploadedFile uploadedFile; private String fileName; public UploadedFile getUploadedFile() { return uploadedFile; } public String getFileName() { return fileName; } public void setUploadedFile(UploadedFile uploadedFile) { this.uploadedFile = uploadedFile; } public void submit() { // Get information you from the uploaded file System.out.println("Uploaded file name: " + uploadedFile.getName()); System.out.println("Uploaded file type: " + uploadedFile.getContentType()); System.out.println("Uploaded file size: " + uploadedFile.getSize() + " bytes"); try { //Upload success FacesContext.getCurrentInstance().addMessage ("uploadForm", new FacesMessage (FacesMessage.SEVERITY_INFO, "File upload was a total success!", null)); } catch (Exception e) { //Upload failed FacesContext.getCurrentInstance().addMessage ("uploadForm", new FacesMessage( FacesMessage.SEVERITY_ERROR, "File upload was a failed.", null)); e.printStackTrace(); } } }
Note that this bean doesn't actually write the uploaded file on server. When the submit
method is called, it contains information about the uploaded file (name, type, size, and so on) and access to its InputStream
through the uploadedFile.getInputStream()
method (another example of exploiting this method can be seen in the recipe Extracting data from an uploaded CVS file). Having the InputStream
and the uploaded filename, you can develop a method as shown next:
public static void write(File file, InputStream input, boolean append) throws IOException { mkdirs(file); BufferedOutputStream output = null; try { output = new BufferedOutputStream( new FileOutputStream(file, append)); int data = -1; while ((data = input.read()) != -1) { output.write(data); } } finally { close(input, file); close(output, file); } }
The append
argument of the write
method indicates if the uploaded content will be appended to the file
file or if it should overwrite the existing one (if there is one).
There are no secrets behind the scenes. It looks like a classic upload component that creates a file-selection widget in the rendered page, which allows a user to select a file for uploading to the server. The upload process is controlled from a bean through an instance of the org.apache.myfaces.custom.fileupload.UploadedFile
class. This object provides us enough information for controlling the uploaded file.
It is not required, but you can configure the Tomahawk ExtensionsFilter
with one or more of the following useful init-param
settings, which you can put in the <filter>
tag (this should appear in the web.xml
file):
<init-param> <description> Set the size limit for uploaded files. Format: 10 - 10 bytes 10k - 10 KB 10m - 10 MB 1g - 1 GB </description> <param-name>uploadMaxFileSize</param-name> <param-value>100m</param-value> </init-param> <init-param> <description> Set the threshold size - files below this limit are stored in memory, files above this limit are stored on disk. Format: 10 - 10 bytes 10k - 10 KB 10m - 10 MB 1g - 1 GB </description> <param-name>uploadThresholdSize</param-name> <param-value>100k</param-value> </init-param> <init-param> <description> Set the path where the intermediary files will be stored. </description> <param-name>uploadRepositoryPath</param-name> <param-value>/temp</param-value> </init-param>