Overview
The JavaServer Faces (JSF) specification provides no direct support for
file uploads. (And, unfortunately, nothing else in the J2EE
technology stack does either.) MyFaces Trinidad provides integrated support
for processing file uploads in a manner natural to JSF applications,
by providing a component that delivers standard
ValueChangeEvents
as files are uploaded, and managing
processing of the uploaded content transparently.
Setup
File upload processing requires the installation of the MyFaces Trinidad Filter.
This filter is required for all MyFaces Trinidad applications. One of its features
is handling file uploads. To install the filter,
first add the following entry to your WEB-INF/web.xml
file:
<filter> <filter-name>trinidad</filter-name> <filter-class>org.apache.myfaces.trinidad.webapp.TrinidadFilter</filter-class> </filter>
Second, map that filter to process all FacesServlet
requests. For example, if you've named the FacesServlet
"faces":
<servlet> <servlet-name>faces</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> </servlet>
...then use the following filter-mapping:
<filter-mapping> <filter-name>trinidad</filter-name> <servlet-name>faces</servlet-name> </filter-mapping>
inputFile
To support uploading a file on your page, use the <tr:inputFile>
component
. Like all other input components, <tr:inputFile>
sends
ValueChangeEvents
. And like all MyFaces Trinidad input components,
it has built-in support for accessibility, labels, and messages.
<tr:inputFile label="Upload:" valueChangeListener="#{backingBean.fileUploaded}"/>
<tr:inputFile>
can be placed in either an
<h:form>
tag or an <tr:form>
tag, but in either case you have to set it to support file
upload. For the JSF Basic HTML form, that involves setting
the "enctype" to the correct magic value; for the MyFaces Trinidad
form, just set "usesUpload" to true:
<tr:form usesUpload="true"> <tr:inputFile label="Upload:" valueChangeListener="#{backingBean.fileUploaded}"/> </tr:form> <!-- or --> <h:form enctype="multipart/form-data"> <tr:inputFile label="Upload:" valueChangeListener="#{backingBean.fileUploaded}"/> </h:form>
The "value"
of an inputFile component is an instance of
the org.apache.myfaces.trinidad.model.UploadedFile
interface. This API lets you get at the actual byte stream of the file, as
well as the file's name, its MIME type, and its size. The
UploadedFile
might be stored as a file in the filesystem,
but might also be stored in memory; this API hides that difference.
The filter ensures that the UploadedFile
content
is cleaned up after the request is complete. Because of this,
you cannot usefully cache UploadedFile
objects across requests.
If you need to keep the file, you must copy it into persistent storage
before the request finishes.
Example
For an example of processing a file upload, instead of actually storing it anywhere, we'll just add a message telling the user that the file was successfully uploaded. The page content is simple:
<tr:form usesUpload="true"> <tr:inputFile label="Upload:" valueChangeListener="#{backingBean.fileUploaded}"/> <tr:commandButton text="Begin"/> </tr:form>
Now, in our backing bean, we'll handle the ValueChangeEvent
:
import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.faces.event.ValueChangeEvent; import org.apache.myfaces.trinidad.model.UploadedFile; public class ABackingBean { ... public void fileUploaded(ValueChangeEvent event) { UploadedFile file = (UploadedFile) event.getNewValue(); if (file != null) { FacesContext context = FacesContext.getCurrentInstance(); FacesMessage message = new FacesMessage( "Successfully uploaded file " + file.getFilename() + " (" + file.getLength() + " bytes)"); context.addMessage(event.getComponent().getClientId(context), message); // Here's where we could call file.getInputStream() } } }
Here, we've chosen to use ValueChangeEvents
. You
could also bind the value of the inputFile directly to a managed
bean:
<tr:form usesUpload="true"> <tr:inputFile label="Upload:" value="#{managedBean.file}"/> <tr:commandButton text="Begin" action="#{managedBean.doUpload}"/> </tr:form>
import org.apache.myfaces.trinidad.model.UploadedFile; public class AManagedBean { public UploadedFile getFile() { return _file; } public void setFile(UploadedFile file) { _file = file; } public String doUpload() { UploadedFile file = getFile(); // ... and process it in some way } private UploadedFile _file; }
Configuration
Because MyFaces Trinidad will temporarily store incoming files (either on
disk or in memory), by default it limits the size of acceptable
incoming requests to avoid denial-of-service attacks that might
attempt to fill a hard drive or flood memory with uploaded files. By
default, only the first 100 kilobytes in any one request will be
stored in memory. Once that has been filled, disk space will be used.
Again, by default, that is limited to 2,000 kilobytes of disk storage
for any one request for all files combined. Once these limits are
exceeded, the filter will throw an EOFException
. Files
are, by default, stored in the temporary directory used by
java.io.File.createTempFile()
, which is usually defined by the
system property java.io.tmpdir
. Obviously, this will be
insufficient for some applications, so you can configure these values
using three servlet context initialization parameters:
<context-param> <!-- Maximum memory per request (in bytes) --> <param-name>org.apache.myfaces.trinidad.UPLOAD_MAX_MEMORY</param-name> <!-- Use 500K --> <param-value>512000</param-value> </context-param> <context-param> <!-- Maximum disk space per request (in bytes) --> <param-name>org.apache.myfaces.trinidad.UPLOAD_MAX_DISK_SPACE</param-name> <!-- Use 5,000K --> <param-value>5120000</param-value> </context-param> <context-param> <!-- directory to store temporary files --> <param-name>org.apache.myfaces.trinidad.UPLOAD_TEMP_DIR</param-name> <!-- Use a TrinidadUploads subdirectory of /tmp --> <param-value>/tmp/TrinidadUploads/</param-value> </context-param> <!-- This filter is always required; one of its functions is file upload. --> <filter> <filter-name>trinidad</filter-name> <filter-class>org.apache.myfaces.trinidad.webapp.TrinidadFilter</filter-class> </filter>
Developers can also customize file upload more comprehensively in trinidad-config.xml
with the following elements:
<uploaded-file-processor>
org.apache.myfaces.trinidad.webapp.UploadedFileProcessor
can be replaced by specifying a new class with the <uploaded-file-processor>
element.
<uploaded-file-max-memory>
org.apache.myfaces.trinidad.UPLOAD_MAX_MEMORY
can be overridden by <uploaded-file-max-memory>
if this param
needs to be bound to an EL expression.
<uploaded-file-max-disk-space>
org.apache.myfaces.trinidad.UPLOAD_MAX_DISK_SPACE
can be overridden by <uploaded-file-max-disk-space>
if this param
needs to be bound to an EL expression.
<uploaded-file-temp-dir>
org.apache.myfaces.trinidad.UPLOAD_TEMP_DIR
can be overridden by <uploaded-file-temp-dir>
if this param
needs to be bound to an EL expression.
Please see the
Configuration
chapter for
more information on these trinidad-config.xml
elements. NOTE:
Replacing the
UploadedFileProcessor
makes the init-params listed
above irrelevant; they are only processed by the default
UploadedFileProcessor
, unless
they are specifically taken
into consideration by the class that replaces the deault file processor class.