About us
Products
Services
Articles
Contact us

10. File Uploading Example

Contents | Previous Chapter | Next Chapter

This chapter presents an example that uses the framework to upload files.


10.1. Data Beans

UploadBean is a Java bean that contains a String (optionalText), a String[] (textGroup), a FileBean (optionalFile) and a FileBean[] (fileList). The class com.devsphere.mapping.FileBean has three read-only properties: fileName, contentType and inputStream.

UploadBean.java:

package com.devsphere.examples.mapping.upload;

import com.devsphere.mapping.FileBean;

/**
 * Upload bean
 */
public class UploadBean implements java.io.Serializable {
    private String optionalText;
    private String textGroup[];
    private FileBean optionalFile;
    private FileBean fileList[];

    /**
     * No-arg constructor
     */
    public UploadBean() {
    }

    /**
     * Gets the optionalText property
     */
    public String getOptionalText() {
        return this.optionalText;
    }

    /**
     * Sets the optionalText property
     */
    public void setOptionalText(String value) {
        this.optionalText = value;
    }

    /**
     * Gets the textGroup property
     */
    public String[] getTextGroup() {
        return this.textGroup;
    }

    /**
     * Sets the textGroup property
     */
    public void setTextGroup(String values[]) {
        this.textGroup = values;
    }

    /**
     * Gets an element of the textGroup property
     */
    public String getTextGroup(int index) {
        return this.textGroup[index];
    }

    /**
     * Sets an element of the textGroup property
     */
    public void setTextGroup(int index, String value) {
        this.textGroup[index] = value;
    }

    /**
     * Gets the optionalFile property
     */
    public FileBean getOptionalFile() {
        return this.optionalFile;
    }

    /**
     * Sets the optionalFile property
     */
    public void setOptionalFile(FileBean value) {
        this.optionalFile = value;
    }

    /**
     * Gets the fileList property
     */
    public FileBean[] getFileList() {
        return this.fileList;
    }

    /**
     * Sets the fileList property
     */
    public void setFileList(FileBean values[]) {
        this.fileList = values;
    }

    /**
     * Gets an element of the fileList property
     */
    public FileBean getFileList(int index) {
        return this.fileList[index];
    }

    /**
     * Sets an element of the fileList property
     */
    public void setFileList(int index, FileBean value) {
        this.fileList[index] = value;
    }

}

10.2. HTML Form

The properties of UploadBean are mapped to the form elements of UploadForm.html:

Name Property type Element type
     
optionalText String text area
textGroup String[] text[]
optionalFile FileBean file
fileList FileBean[] file[]

The user may fill the text elements, select one or more files and then click Submit. The browser will encode the inputted text and the files' content and send them to the Web server using the "multipart/form-data" media type, which is the value of the ENCTYPE attribute of the <FORM> tag. The use of this encoding is necessary if you want to upload files.

Note: Only com.devsphere.mapping.FileBean objects should be mapped to file input elements. FileBean objects cannot be mapped to text or XML.

There are interesting differences between the three text fields and the three file elements. All of the text fields must have a value. Otherwise an error is signaled for each text field whose value is missing and the values of the other fields are preserved. The file elements are treated as a list, which may contain 1-3 files. If the list is empty a single error message is shown for the entire list. Also each of the text fields may have its own default value while the file elements cannot.

The way the mapping between JavaBeans and HTML forms works was explained in a previous chapter.

UploadForm.html:

<HTML>
<HEAD><TITLE>Upload Form</TITLE></HEAD>
<BODY>
<H3>File Uploading Example</H3>
<FORM METHOD="POST" ENCTYPE="multipart/form-data">
<P> Optional Text <BR>
<TEXTAREA NAME="optionalText" ROWS="5" COLS="30">
</TEXTAREA>
<P> Text group <BR>
<INPUT TYPE="TEXT" NAME="textGroup" SIZE="20"> <BR>
<INPUT TYPE="TEXT" NAME="textGroup" SIZE="20"> <BR>
<INPUT TYPE="TEXT" NAME="textGroup" SIZE="20"> <BR>
<P> Optional file <BR>
<INPUT TYPE="FILE" NAME="optionalFile">
<P> File list <BR>
<INPUT TYPE="FILE" NAME="fileList"> <BR>
<INPUT TYPE="FILE" NAME="fileList"> <BR>
<INPUT TYPE="FILE" NAME="fileList"> <BR>
<P>
<INPUT TYPE="SUBMIT" VALUE="Submit">
<INPUT TYPE="RESET" VALUE="Reset">
</FORM>
<P> Warning! <BR>
This example ignores the HTTP request if the length of the posted data
exceeds 400,000 bytes.
</BODY>
</HTML>

10.3. Bean Resources

The UploadBeanResources.properties file contains some default values, the list of optional properties and the names of the two JSP files described in the next sections.

UploadBeanResources.properties:

[DEFAULT_VALUE.optionalText]=123
[DEFAULT_VALUE.textGroup.length]=3
[DEFAULT_VALUE.textGroup.1]=abc
[OPTIONAL_PROPERTIES]=optionalText optionalFile
[FORM_NAME]=UploadForm.html
[PROC_NAME]=UploadProc.jsp

10.4. JSP Handler

The UploadHndl.jsp handler is based on a template that was described in a previous chapter and is very similar to the handler of the first example (SimpleHndl.jsp).

The Servlet API does not support the "multipart/form-data" encoding type, which is necessary for file uploading. The com.devsphere.mapping.ServletFormData class can parse the input stream of the wrapped javax.servlet.http.HttpServletRequest to get a parameter set that includes strings, string arrays, FileBean objects and FileBean arrays.

The used parser is provided by JavaMail and it wasn't designed for uploading large files. The uploaded files are decoded and their content is kept in memory. Therefore, the file-uploading feature should be used only when reasonable amounts of data need to be transferred for in-memory processing.

To enable the "multipart/form-data" parsing, you must use the constructor of ServletFormData that accepts two parameters, pass a HttpServletRequest object whose content-type starts with "multipart/form-data" and the second parameter (allowMultipart) must be true. If these conditions aren't met the multipart parser isn't enabled, the getParameter*() methods are delegated to the HttpServletRequest object and the getFileParameter*() methods return null (or an empty enumeration in the case of getFileParameterNames()).

To ensure that the server's performance doesn't drop under a certain point you should ignore any HTTP request whose content-length exceeds an experimentally determined limit. Extensive testing that simulate the production environment should be performed in order to determine a safe value for that limit. Also you could limit the number of file uploadings that could be performed at the same time.

The JSP handler of this example arbitrarily limits the content-length of the processed requests to 400,000.

We recommend the use of another protocol, such as FTP, for transferring large files.

UploadHndl.jsp:

<%@ page language="java" %>
<%@ page import="com.devsphere.mapping.*, com.devsphere.logging.*" %>
<jsp:useBean id="uploadBean" scope="request"
    class="com.devsphere.examples.mapping.upload.UploadBean"/>
<%
// Determine the HTTP method
boolean isPostMethod = request.getMethod().equals("POST");

// Verify the content length
int contentLength = request.getContentLength();
if (isPostMethod && (contentLength < 0 || contentLength > 400000))
    application.log("Request ignored. Content-Length: " + contentLength);
else {
    // Get the bean resources
    java.util.ResourceBundle beanRes
        = HandlerUtils.getBeanResources(uploadBean.getClass());

    // Construct the base path
    String basePath = request.getServletPath();
    int slashIndex = basePath.lastIndexOf('/');
    basePath = slashIndex != -1 ? basePath.substring(0, slashIndex+1) : "";

    // Create a logger that wraps the servlet context
    ServletLogger logger = new ServletLogger(application);

    // Wrap the form data
    FormData formData = new ServletFormData(request, true);

    // Form-to-bean mapping: request parameters are mapped to bean properties
    java.util.Hashtable errorTable
        = FormUtils.formToBean(formData, uploadBean, logger);

    if (isPostMethod && errorTable == null) {
        // Construct the processor's path
        String procPath = basePath + beanRes.getString("[PROC_NAME]").trim();

        // Process the valid data bean instance
        application.getRequestDispatcher(procPath).forward(request, response);
    } else {
        if (!isPostMethod)
            // Ignore the user errors if the form is requested with GET.
            errorTable = HandlerUtils.removeUserErrors(errorTable);

        // Construct the form's path
        String formPath = basePath + beanRes.getString("[FORM_NAME]").trim();
        formPath = application.getRealPath(formPath);

        // Get the form template
        FormTemplate template = FormUtils.getTemplate(new java.io.File(formPath));

        // Get a new document
        FormDocument document = template.getDocument();

        // Bean-to-form mapping: bean properties are mapped to form elements
        FormUtils.beanToForm(uploadBean, errorTable, document, logger);

        // Send the form document
        document.send(out);
    }
}
%>

10.5. JSP Processor

The UploadProc.jsp processor gets only valid bean objects and outputs the values of their properties. Also the uploaded files are saved to disk.

UploadProc.jsp:

<%@ page language="java" %>
<%@ page import="java.io.*, com.devsphere.mapping.*" %>
<jsp:useBean id="uploadBean" scope="request"
    class="com.devsphere.examples.mapping.upload.UploadBean"/>
<%
    byte buf[] = new byte[1024 * 4];
    String dir = request.getServletPath();
    int slashIndex = dir.lastIndexOf('/');
    dir = slashIndex != -1 ? dir.substring(0, slashIndex+1) : "";
    dir = application.getRealPath(dir + "uploaded");
%>
<HTML>
<HEAD><TITLE>Upload Form</TITLE></HEAD>
<BODY>
<H3>File Uploading Example</H3>
<P><B></B>
<P><B> UploadBean properties: </B>
    <P> optionalText = <%= uploadBean.getOptionalText() %>
    <P> textList[0] = <%= getItem(uploadBean.getTextGroup(), 0) %>
    <P> textList[1] = <%= getItem(uploadBean.getTextGroup(), 1) %>
    <P> textList[2] = <%= getItem(uploadBean.getTextGroup(), 2) %>
    <P> optionalFile = <%= saveFile(uploadBean.getOptionalFile(), dir, buf) %>
    <P> fileList[0] = <%= saveFile(uploadBean.getFileList(), 0, dir, buf) %>
    <P> fileList[1] = <%= saveFile(uploadBean.getFileList(), 1, dir, buf) %>
    <P> fileList[2] = <%= saveFile(uploadBean.getFileList(), 2, dir, buf) %>
</BODY>
</HTML>

<%!
    // Gets the item of list
    String getItem(String list[], int index) {
        if (list == null || index >= list.length)
            return "";
        return list[index];
    }

    // Saves a file and returns its name
    String saveFile(FileBean fileBeanArray[], int index, String dir, byte buf[])
        throws IOException {
        if (fileBeanArray == null || index >= fileBeanArray.length)
            return "";
        return saveFile(fileBeanArray[index], dir, buf);
    }

    // Saves a file and returns its name
    String saveFile(FileBean fileBean, String dir, byte buf[])
        throws IOException {
        if (fileBean == null)
            return "";
        new File(dir).mkdirs();
        File file = new File(dir, fileBean.getFileName());
        FileOutputStream output = new FileOutputStream(file);
        try {
            InputStream input = fileBean.getInputStream();
            try {
                while (true) {
                    int count = input.read(buf);
                    if (count == -1)
                        break;
                    output.write(buf, 0, count);
                }
            } finally {
                input.close();
            }
        } finally {
            output.close();
        }
        return fileBean.getFileName();
    }
%>

10.6. Hints and Tips

When you use file uploading, make sure that

  • the value of the ENCTYPE attribute of the <FORM> tag is "multipart/form-data"
  • you use the ServletFormData(HttpServletRequest request, boolean allowMultipart) constructor
  • the value of the allowMultipart flag is true

Also, keep in mind that the content of the uploaded files is kept in memory on the server side until the ServletFormData object is garbage collected.

Contents | Previous Chapter | Next Chapter

Copyright © 2000-2017 Devsphere

About us
Products
Services
Articles
Contact us