Skip Headers
Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3)
B25386-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

11.6 Providing File Upload Capability

File uploading is a capability that is required in many web applications. Standard J2EE technologies such as Servlets and JSP, and JSF 1.1.x, do not directly support file uploading. The ADF Faces framework, however, has integrated file uploading support at the component level via the inputFile component.

During file uploading, ADF Faces temporarily stores incoming files either in memory or on disk. You can set a default directory storage location, and default values for the amount of disk space and memory that can be used in any one file upload request.

Figure 11-16 shows the SRMain.jspx page of the SRDemo application, where users can upload files for a particular service request.

Figure 11-16 File Upload Button on the SRMain Page

Upload function on service request information page

When the user clicks Upload document, the upload form displays in a popup dialog, as shown in Figure 11-17.

Figure 11-17 File Upload Form in the SRDemo Application

File upload form for entering file

The user can enter the full pathname of the file for uploading or click Browse to locate and select the file. When Begin Upload is clicked, ADF Faces automatically uploads the selected file. Upon a successful upload, ADF Faces displays some information about the uploaded file, as shown in Figure 11-18. If uploading is unsuccessful for some reason, the application displays the error stack trace in the same popup dialog.

Figure 11-18 File Upload Success Information

File upload form with success message box

11.6.1 How to Support File Uploading on a Page

Use the following tasks to provide file uploading support in a JSF application.

To provide file uploading support:

  1. Make sure the ADF Faces filter has been installed.

    The ADF Faces filter is a servlet filter that ensures ADF Faces is properly initialized by establishing an AdfFacesContext object. JDeveloper automatically installs the filter for you in web.xml when you insert an ADF Faces component into a JSF page for the first time. Example 11-45 shows the ADF Faces filter and mapping configuration setting in web.xml.

    Example 11-45 ADF Faces Filter in the web.xml File

    <!-- Installs the ADF Faces Filter -- >
    <filter>
      <filter-name>adfFaces</filter-name>
      <filter-class>oracle.adf.view.faces.webapp.AdfFacesFilter</filter-class>
    </filter>
    
    <!-- Adds the mapping to ADF Faces Filter -- >
    <filter-mapping>
      <filter-name>adfFaces</filter-name>
      <servlet-name>Faces Servlet</servlet-name>
    </filter-mapping>
    
    
  2. In web.xml set a context initialization parameter for the storage location of uploaded files. It's up to you where you want to save the uploaded files. Example 11-46 shows the context parameter used in the SRDemo application for uploaded files.

    Example 11-46 Uploaded File Storage Location in the web.xml File

    <context-param>
      <description>Parent directory location of SRDemo fileuploads</description>
      <param-name>SRDemo.FILE_UPLOADS_DIR</param-name>
      <param-value>/tmp/srdemo_fileuploads</param-value>
    </context-param>
    
    
  3. Create a backing bean for handling uploaded files. Example 11-47 shows the managed bean code in faces-config.xml for the SRDemo file upload page.

    Example 11-47 Managed Bean for the SRFileUpload Page in the faces.config.xml File

    <managed-bean>
      <managed-bean-name>backing_SRFileUpload</managed-bean-name>
      <managed-bean-class>
        oracle.srdemo.view.backing.SRFileUpload</managed-bean-class>
      <managed-bean-scope>request</managed-bean-scope>
      ...
    </managed-bean>
    
    
  4. In the JSF page you can use either af:form or h:form for file uploading. Make sure you set the enclosing form to support file uploads, as shown in the next code snippet:

    <af:form usesUpload="true"/>
    ..
    <h:form enctype="multipart/form-data"/>
    
    
  5. Use the inputFile component to provide a standard input field with a label, and a Browse button, as shown in Figure 11-17.

    The inputFile component delivers standard value change events as files are uploaded, and manages the processing of the uploaded contents for you. It is up to you how you want to handle the contents.

    To process file uploading, you could either implement a value change listener method in the backing bean to handle the event, or bind the value attribute of inputFile directly to a managed bean property of type oracle.adf.view.faces.model.UploadedFile. Either way you have to write your own Java code in the backing bean for handling the uploaded files.

    The following code snippet shows the code for an inputFile component if you were to bind the component to a managed bean property of type oracle.adf.view.faces.model.UploadedFile.

    <af:inputFile value="#{myuploadBean.myuploadedFile}".../>
    
    

    The SRDemo file upload form uses a value change listener method. Example 11-48 shows the code for the method binding expression in the valueChangeListener attribute of the inputFile component.

    Example 11-48 InputFile Component in the SRFileUpload.jspx File

    <af:inputFile label="#{res['srfileupload.uploadlabel']}"
                  valueChangeListener="#{backing_SRFileUpload.fileUploaded}"
                  binding="#{backing_SRFileUpload.srInputFile}"
                  columns="40"/>
    
    
  6. In the page's backing bean, write the code for handling the uploaded contents. For example, you could write the contents to a local directory in the file system. Example 11-49 shows the value change listener method that handles the value change event for file uploading in the SRDemo application.

    Example 11-49 Value Change Listener Method for Handling a File Upload Event

    public void fileUploaded(ValueChangeEvent event) {
    
        InputStream in;
        FileOutputStream out;
        
        // Set fileUPloadLoc to "SRDemo.FILE_UPLOADS_DIR" context init parameter
        String fileUploadLoc = FacesContext.getCurrentInstance().getExternalContext().getInitParameter("SRDemo.FILE_UPLOADS_DIR");
        
        if (fileUploadLoc == null) {
           // Backup value if context init parameter not set.
          fileUploadLoc = "/tmp/srdemo_fileuploads";
        }
                
        
       //get svrId and append to file upload location
       Integer svrId = (Integer)JSFUtils.getManagedBeanValue("userState.currentSvrId");
       fileUploadLoc += "/sr_" + svrId + "_uploadedfiles";
       
     
        // Create upload directory if it does not exists.
        boolean exists = (new File(fileUploadLoc)).exists();
        if (!exists) {
            (new File(fileUploadLoc)).mkdirs();
        }
     
     
        UploadedFile file = (UploadedFile)event.getNewValue();
    
            
        if (file != null && file.getLength()>0) {
            FacesContext context = FacesContext.getCurrentInstance();
            FacesMessage message =
                        new FacesMessage(JSFUtils.getStringFromBundle("srmain.srfileupload.success")+" "+
                                       file.getFilename() + " (" +
                                       file.getLength() +
                                       " bytes)");
            context.addMessage(event.getComponent().getClientId(context),
                               message);
     
            try {
                out =
                    new FileOutputStream(fileUploadLoc + "/" + file.getFilename());
                in = file.getInputStream();
                
                for (int bytes = 0; bytes < file.getLength(); bytes++) {
                  out.write(in.read());
                }  
                  
                in.close();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        else {
            // need to check for null value here as otherwise closing
            // the dialog after a failed upload attempt will lead to
            // a nullpointer exception
            String filename = file != null ? file.getFilename() : null;
            String byteLength = file !=null ? "" + file.getLength() : "0";
            
            FacesContext context = FacesContext.getCurrentInstance();
            FacesMessage message =
            new FacesMessage(FacesMessage.SEVERITY_WARN, JSFUtils.getStringFromBundle("srmain.srfileupload.error") + " " + 
                            filename + " (" + byteLength + " bytes)", null);
            context.addMessage(event.getComponent().getClientId(context),message);
        }
    }
    
    
  7. Use a commandButton component to submit the form. Example 11-50 shows the commandButton code in the SRDemo file upload form, and also the action method code in the page's backing bean.

    Example 11-50 Code for the Command Button and Action Method

    <af:commandButton text="#{res['srfileupload.uploadbutton']}"
                      action="#{backing_SRFileUpload.UploadButton_action}"/>
    
    ...
    ...
    public String UploadButton_action() {
        if (this.getSrInputFile().getValue() == null){                        
            FacesContext context = FacesContext.getCurrentInstance();
            FacesMessage message =
            new FacesMessage(FacesMessage.SEVERITY_WARN, JSFUtils.getStringFromBundle("srmain.srfileupload.emptyfielderror"), null);
            context.addMessage(this.getSrInputFile().getId(), message);
                
        }
        
        return null;
    }
    
    
  8. If using a popup dialog, add a commandLink component to let the user close the dialog. For more information about closing a popup dialog, see Section 11.3.1.3, "Creating the Dialog Page and Returning a Dialog Value". Example 11-51 shows the code for the commandLink component and the action method in the page's backing bean.

    Example 11-51 Code for the Command Link and Action Method

    <af:commandLink action="#{backing_SRFileUpload.closeFileUpload_action}"../>
    ..
    public String closeFileUpload_action() {
      AdfFacesContext.getCurrentInstance().returnFromDialog(null, null);
      return null;
    }
    
    

11.6.2 What Happens at Runtime

The SRDemo application creates a directory such as C:\tmp\srdemo_fileuploads to store uploaded files. Uploaded files for a service request are placed in a subdirectory prefixed with the service request id, for example C:\tmp\srdemo_fileuploads\sr_103_uploadedfiles.

The oracle.adf.view.faces.webapp.UploadedFileProcessor API is responsible for processing file uploads. Each application has a single UploadedFileProcessor instance, which is accessible from AdfFacesContext.

The UploadedFileProcessor processes each uploaded file as it comes from the incoming request, converting the incoming stream into an oracle.adf.view.faces.model.UploadedFile instance, and making the contents available for the duration of the current request. In other words, the value attribute of the inputFile component is automatically set to an instance of UploadedFile. If the inputFile component's value is bound to a managed bean property of type oracle.adf.view.faces.model.UploadedFile, ADF Faces sets an UploadedFile object on the model.

The oracle.adf.view.faces.model.UploadedFile API describes the contents of a single file. It 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 file system, or it might be stored in memory; the API hides that difference.

ADF Faces 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 are stored in memory. Once that has been filled, disk space is used. Again, by default, that is limited to 2,000 kilobytes of disk storage for any one request for all files combined. The AdfFacesFilter throws an EOFException once the default disk storage and memory limits are reached. To change the default values, see Section 11.6.4, "Configuring File Uploading Initialization Parameters".

11.6.3 What You May Need to Know About ADF Faces File Upload

Consider the following if you're using ADF Faces file upload:

  • Most applications don't need to replace the default UploadedFileProcessor instance, but if your application needs to support uploading of very large files, you may wish to replace the default processor with a custom UploadedFileProcessor implementation. For more information see Section 11.6.5, "Configuring a Custom Uploaded File Processor".

  • The ADF Faces Filter ensures that the UploadedFile content is cleaned up after the request is complete. Thus, you cannot cache UploadedFile objects across requests. If you need to keep a file, you must copy it into persistent storage before the request finishes.

11.6.4 Configuring File Uploading Initialization Parameters

During file uploading, ADF Faces temporarily stores incoming files either on disk or in memory. ADF Faces defaults to the application server's temporary directory, as provided by the javax.servlet.context.tempdir property. If that property is not set, the system java.io.tempdir property is used.

If you wish you can set a default temporary storage location, and default values for the amount of disk space and memory that can be used in any one file upload request. You can specify the following file upload context parameters in web.xml:

  • oracle.adf.view.faces.UPLOAD_TEMP_DIR—Specifies the directory where temporary files are to be stored during file uploading. Default is the user's temporary directory.

  • oracle.adf.view.faces.UPLOAD_MAX_DISK_SPACE—Specifies the maximum amount of disk space that can be used in a single request to store uploaded files. Default is 2000K.

  • oracle.adf.view.faces.UPLOAD_MAX_MEMORY—Specifies the maximum amount of memory that can be used in a single request to store uploaded files. Default is 100K.

Example 11-52 shows the context initialization parameters for file uploading that you use in web.xml.

Example 11-52 Context Parameters for File Uploading in the web.xml File

<context-param>
  <param-name>oracle.adf.view.faces.UPLOAD_TEMP_DIR</param-name>
  <param-value>/tmp/Adfuploads</param-value>
</context-param>

<context-param>
  <param-name>oracle.adf.view.faces.UPLOAD_MAX_DISK_SPACE</param-name>
  <param-value>10240000</param-value>
</context-param>

<context-param>
  <param-name>oracle.adf.view.faces.UPLOAD_MAX_MEMORY</param-name>
  <param-value>5120000</param-value>
</context-param>

Note:

The file upload initialization parameters are processed by the default UploadedFileProcessor only. If you replace the default processor with a custom UploadedFileProcessor implementation, the parameters are not processed.

11.6.5 Configuring a Custom Uploaded File Processor

Most applications don't need to replace the default UploadedFileProcessor instance provided by ADF Faces, but if your application needs to support uploading of very large files or rely heavily on file uploads, you may wish to replace the default processor with a custom UploadedFileProcessor implementation. For example, you could improve performance by using an implementation that immediately stores files in their final destination, instead of requiring ADF Faces to handle temporary storage during the request.

To replace the default processor, specify the custom implementation using the <uploaded-file-processor> element in adf-faces-config.xml. Example 11-53 shows the code for registering a custom UploadedFileProcessor implementation.

Example 11-53 Registering a Custom Uploaded File Processor in the adf-faces-config.xml File

<adf-faces-config xmlns="http://xmlns.oracle.com/adf/view/faces/config">
...
  <!-- Use my UploadFileProcessor class -->
  <uploaded-file-processor>
    com.mycompany.faces.myUploadedFileProcessor
  </uploaded-file-processor>
...
</adf-faces-config>

Tip:

Any file uploading initialization parameters specified in web.xml are processed by the default UploadedFileProcessor only. If you replace the default processor with a custom UploadedFileProcessor implementation, the file uploading parameters are not processed.