ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Uploading Files with Beans
Pages: 1, 2, 3, 4

The content of the abisco.html file is as follows.



<html>
<head>
<title>Abisco</title>
</head>
</html>

Screen shot.
Figure 1. The user interface for the example.

When you click the Upload button (see Figure 1 again), the form will be sent. This includes the file called abisco.html. This form is submitted to the Jsp1.jsp file. This JSP file itself does not return any response to the browser. However, it creates a file called Demo.out.

Open the Demo.out file, and you will see the following.

-----------------------------7d15340138
Content-Disposition: form-data; name="Author"

A. Christie
-----------------------------7d15340138
Content-Disposition: form-data; name="Company"

Abisco
-----------------------------7d15340138
Content-Disposition: form-data; name="Filename"; filename="C:\123data\abisco.html"
Content-Type: text/html

<html>
<head>
<title>Abisco</title>
</head>
</html>
-----------------------------7d15340138--

In brief, the entity body of the HTTP request contains all the form input, including the uploaded file. Those input values are separated from each other by a delimiter. Sometimes called a boundary, this delimiter consists of a few dozen dash characters followed by a random number. In the example above, the delimiter is the following line.

-----------------------------7d15340138

The last delimiter ends the entity body with two more dashes.

For an input value that comes from a non-file element, the delimiter is follows by the following line.

Content-Disposition: form-data; name=inputName

where inputName is the name of the form element.

For example:

Content-Disposition: form-data; name="Author"

Two sequences of carriage return linefeed characters and the element value follow the line.

For the file, two lines follow the delimiter. The first contains the name of the FILE input element and the complete path of the file in the user's computer. From the HTTP request above, this line is as follows.

Content-Disposition: form-data; name="Filename"; filename="C:\123data\abisco.html"

It states that the FILE input element is called Filename and the file path is C:\123data\abisco.html. Note that Windows browsers include the file path, but Unix/Linux and Mac browsers only send the filename.

The second line contains the content type of the file. Therefore, its value depends on the file being uploaded. For this example, the value is text/html.

Content-Type: text/html

Like the non-File input element, the content starts after two sequences of carriage return linefeed characters.

That's all you need to know to write a File Upload Bean.

Brainy FileUpload Bean

Some file upload code is written as part of a bigger application. Often the piece of code that does the extraction of the uploaded file is integrated with the other pieces as a whole, making it unusable for other applications. The solution here gives you the file upload function in a Bean, which we all know is a component in the Java platform. As such, this Bean is portable and can be used in any application that requires file upload.

I call this Bean Brainy FileUploadBean. The complete code is given in Listing 1. This section gives you the detailed descriptions of what the code does, so that you can extend the functionality if you so desire.

The first line of code is the package information. When I wrote the Bean, it was part of the package called com.brainysoftware.web. You probably don't want to use the same name for the package; therefore you should change the first line of the code.

package com.brainysoftware.web;

Alternatively, you can remove this line entirely if you don't want the class to be a member of any package.

Next are the import statements that tell you what classes or interfaces are used in the Bean. Of special interest are the HttpServletRequest interface from the javax.servlet.http package and the abstract class ServletInputStream from the javax.servlet package. This class is a subclass of java.io.InputStream.

Also imported are Dictionary and Hashtable from the java.util package and PrintWriter, BufferedWriter, FileWriter, and IOException from the java.io package.

Then comes the class, FileUploadBean. The class has 5 private fields, 6 public methods and 2 private methods.

The FileUploadBean Class's Fields

The five fields of the FileUploadBean class are all private. They are as follows.

  • private String savePath
    This field specifies the path where the uploaded file should be saved on the server. You set the value of savePath using the setSavePath method. You should set this value prior to calling the doUpload method. If not set, the uploaded file will be saved in the default directory on the server.
  • private String filepath
    This field specifies the complete path of the uploaded file on the client side. The value of this field is specified by the doUpload method. You can obtain this field's value using the getFilepath method from your JSP page or Servlet. For non-Windows browsers, this value is the same as filename.
  • private String filename
    This field specifies the name of the uploaded file. This field is set by the setFilename method and retrieved from the filepath value. You can get the filename from your Servlet or JSP page using the getFilename value.
  • private String contentType
    This field specifies the type of the uploaded file content. Set by the doUpload method, you can get the value of this field using the getContentType method.
  • private Dictionary fields
    This Dictionary stores the name/value pairs of the HTML form's input elements at the client side. You can then obtain the value of an input element by calling the getFieldValue method.

The FileUploadBean Class's Methods

The first four public methods are used to return the FileUploadBean object's private fields. They are getFilepath, getFilename, getContentType and getFieldValue.

  • public String getFilepath()
    This method returns the value of the filepath private field.
  • public String getFilename()
    This method returns the value of the filename private field.
  • public String getContentType()
    This method returns the value of the contentType private field.
  • public String getFieldValue(String fieldName)
    This method returns the value of the HTML form element whose name is specified by fieldName.
  • public void setSavePath(String savePath)
    Use this method to specify the name of the directory where the uploaded file should be saved on the server.
  • >public void doUpload(HttpServletRequest request) throws IOException
    This is the most important method in the FileUploadBean class. This method does two things. First, it extracts the field names and values from the HTML form. All the field name-value pairs are stored in the fields Hashtable object which is upcast to a Dictionary. The second thing the doUpload method does is extract the uploaded file and save it to the directory specified by savePath and assign the file's name, path and content type to filename, filepath, and contentType respectively.

You pass the HttpServletRequest object created by the Servlet/JSP container to the doUpload method. This object represents the HTTP request that you need to process to extract the HTML form's element names-values and the uploaded file. The method starts by obtaining the ServletInputStream object using the getInputStream method of the HttpServletRequest object.

As explained before, each form element is separated by the boundary and a sequence of carriage return line feed characters. Therefore, you can read the content of the HttpServletRequest object line by line. The following line of code defines a byte array called line.

byte[] line = new byte[128];

You then use the readLine method of ServletInputStream to read the first line of the HttpServletRequest object's content.

int i = in.readLine(line, 0, 128);

The first line should be the boundary, and its length, if no error has occurred, should be much longer than 3. Therefore, a length of less than three can be assumed erroneous and the doUpload method should exit.

if (i < 3)
  return;

The boundary and the length of the boundary are important values, as you will see later. The boundary is terminated by a sequence of carriage return linefeed characters. Therefore the actual length is two less than the number of bytes returned by the readLine method.

int boundaryLength = i - 2;

The boundary is retrieved from the byte array line by discarding the last two carriage return linefeed characters.

String boundary = new String(line, 0, boundaryLength);

The field fields is then instantiated with a Hashtable object. This Hashtable object is used to store the HTML form element name/value pairs.

fields = new Hashtable();

Having the boundary you can then start extracting the form element value by reading the HttpServletRequest object's content line by line using the while loop, until it reaches the end of it when the readLine method returns -1. All HTML form elements start with a boundary followed by the content-disposition line that starts with the following characters.

Content-Disposition: form-data; name=

There are two types of form elements: file and non-file (normal form elements such as TEXT or HIDDEN elements). The difference between the two is based on the file element, which contains the following string.

filename="filename"

Therefore, you can use this information to distinguish the file from the non-file input elements using the following two if statements.

if (newLine.startsWith("Content-Disposition: form-data; name=\"")) {
if (newLine.indexOf("filename=\"") != -1) {
// a file form element, code to extract the file here

. . .

}
else {
//this is a field, code to extract fields here

. . .

}
}

Now, let's a look at the code to extract the file content first. Afterwards, we have a look at the code to extract non-file form elements.

The filepath is contained in the content-disposition. To retrieve the file path and the filename, the doUpload method calls the setFilename private method. This method will be discussed in the next section but it is sufficient to say that setFilename extracts the file path and the filename information and assign them to the filepath and filename fields.

After the setFilename method call, filename should not be null. Otherwise, an error has occurred and the doUpload method returns entirely.

if (filename==null)
  return;

The next line after the content-disposition line is the content type line. So call the readLine method again and call the setContentType private method. This method is similar to setFilename. It retrieves the content type of the uploaded file from the raw bytes and assign it contentType.

The next line after the line that contains the file content type is a blank line. Therefore, call the readLine method again.

i = in.readLine(line, 0, 128);

Then, begins the actual file content. We should be ready to write the file to the disk, using a PrinterWriter object.

PrintWriter pw = new PrintWriter(new BufferedWriter(
new FileWriter(
( savePath==null? "" : savePath ) + filename
)));

Where the file should be saved to depends on whether the savePath field has been set. If the savePath was not set, its value is null, and the file should be saved in the default directory. If the savePath field has been set, its value will not be null so the uploaded file must be uploaded to this directory.

We can then extract the file content by using a while loop that reads one line at a time and writes it to disk using the write method of the PrintWriter. However, we know that the last line of the file includes two carriage return line feed characters. Therefore, the bytes saved to the disk must not include these two characters. So if the line read is not the last line of the file, write all the bytes read to the disk. If it is, write all the bytes read minus the last two characters.

However, we do not know the size of the file. What we know is that the next line after the file content is another boundary. Or, if the file is the last HTML form element, the next line is the boundary plus two dash characters. Therefore, by checking whether or not the next line is a boundary we will know how to exit from the while loop. This is the reason why I said the boundary value was important. We need the boundary value for this.

We could read the next line and use the startsWith method to check if the next line is a boundary. However, string manipulation is expensive. To avoid string manipulation, we compare the length of the byte array read by readLine. The latter should be equal to boundaryLength + 2. Or, if it is the last line in the HttpServletRequest object, it should be equal to boundaryLength + 4 because of the last two dash characters. Because a line could also be as long as a boundary without being a boundary, we compare it with the boundary when the length matches. Now you know why the boundaryLength value is also important.

Pages: 1, 2, 3, 4

Next Pagearrow