Processing file uploads

Overview

The JavaServer Faces (JSF) 1.1 specification provides no direct support for file uploads. (And, unfortunately, nothing else in the J2EE technology stack does either.) ADF Faces 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 ADF Faces Filter. This filter is required for all ADF Faces 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>adfFaces</filter-name>
    <filter-class>oracle.adf.view.faces.webapp.AdfFacesFilter</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>adfFaces</filter-name>
    <servlet-name>faces</servlet-name>
  </filter-mapping>

inputFile

To support uploading a file on your page, use the < af:inputFile > component . Like all other input components, < af:inputFile > sends ValueChangeEvents . And like all ADF Faces input components, it has built-in support for accessibility, labels, and messages.

       <af:inputFile label="Upload:"
                     valueChangeListener="#{backingBean.fileUploaded}"/>
                          

< af:inputFile > can be placed in either an < h:form > tag or an < af: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 ADF Faces form, just set "usesUpload" to true:

     <af:form usesUpload="true">
       <af:inputFile label="Upload:"
                     valueChangeListener="#{backingBean.fileUploaded}"/>
     </af:form>
     <!-- or -->
     <h:form enctype="multipart/form-data">
       <af:inputFile label="Upload:"
                     valueChangeListener="#{backingBean.fileUploaded}"/>
     </h:form>

The "value" of an inputFile component is an instance of the oracle.adf.view.faces.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 h ides 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:

     <af:form usesUpload="true">
       <af:inputFile label="Upload:"
                     valueChangeListener="#{backingBean.fileUploaded}"/>
       <af:commandButton text="Begin"/>
     </af: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 oracle.adf.view.faces.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:

     <af:form usesUpload="true">
       <af:inputFile label="Upload:" value="#{managedBean.file}"/>
       <af:commandButton text="Begin" action="#{managedBean.doUpload}"/>
     </af:form>
import oracle.adf.view.faces.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 ADF Faces 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>oracle.adf.view.faces.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>oracle.adf.view.faces.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>oracle.adf.view.faces.UPLOAD_TEMP_DIR</param-name>
    <!-- Use an ADFUploads subdirectory of /tmp -->
    <param-value>/tmp/ADFUploads/</param-value>
  </context-param>

  <!-- This filter is always required by ADF;  one of its functions is 
          file upload. -->
  <filter>
    <filter-name>adfFaces</filter-name>
    <filter-class>oracle.adf.view.faces.webapp.AdfFacesFilter</filter-class>
  </filter>

Developers can also customize file upload more comprehensively by replacing the entire oracle.adf.view.faces.webapp.UploadedFileProcessor with the < uploaded-file-processor > element in adf-faces-config.xml . Please see the Configuration chapter for more information on that element. Replacing the UploadedFileProcessor makes the init-params listed above irrelevant; they are only processed by the default UploadedFileProcessor .

Copyright 2003-2006, Oracle Corporation. All Rights Reserved.