Chapter 15. Handling Files on the Server

Welcome to Day 15! Today you'll see one of the most important topics in JSP programming—storing data on the server. One of the main reasons that programmers switch from client-based applications (such as those written in JavaScript that execute in the browser) to JSP is to be able to store and work with data in files (which you can't do in JavaScript). For example, you may want to create a guest book that stores user comments on the server, or even create and work with your own database. You might even want to log errors or user accesses—any data that you want to persist between sessions, you can store in files on the server. There are literally thousands of uses for working with files on the server.

Here are the topics you'll see today:

  • Understanding Java file I/O

  • Understanding I/O streams

  • Using Java readers and writers

  • Working with text files

  • Working with binary files

  • Creating a guest book

  • Logging data on the server

  • Getting a list of the files in a directory

Today's work will start with a topic you were first introduced to yesterday—using Tomcat log files.

Note

As you'll see today, working with files in Java is a large and complex subject. There is far more material than can be covered here, so if you need more information, take a look at the Java documentation, or pick up a book on Java.

Using Tomcat Logging

You learned how to write to a Tomcat log file using a filter yesterday; here's an example showing how to do that from JSP. First, you set up a log file for the examples in the ch15 directory on the server in Tomcat's server.xml file. This is accomplished with a new <Context> element (it's inside the <Host> element in server.xml). You place this <Context> element after the other <Context> elements in server.xml:

<Context path="/ch15" docBase="ch15" debug="0" reloadable="true"> 
    <Logger className="org.apache.catalina.logger.FileLogger"
        directory="logs"  prefix="ch15." suffix=".txt"
         timestamp="true"/>
</Context>

That sets up the log file, which will be stored in the jakarta-tomcat-4.0.3logs directory and have the prefix ch15. Now you can use the servlet context's log method to write to the log file (you can get access to the servlet context with the session object's getServletContext method). For example, to log the IP address of the user accessing this JSP page, and the URL to which she has navigated, you can use the code you see in Listing 15.1.

Example 15.1. Writing to a Log File (ch15_01.jsp)

<HTML>
    <HEAD>
        <TITLE>Using Log Files</TITLE>
    </HEAD>

    <BODY>
        <H1>Using Log Files</H1>
        This page writes to a log file.
        <%
            String address =  request.getRemoteAddr();
            String file = ((HttpServletRequest) request).getRequestURI();

            session.getServletContext().log(
                "User access! " +
                " User IP: " + address +
                " Resource: " + file
            );
        %>
    </BODY>
</HTML>

You can see this page in Figure 15.1, where you see a message indicating that this JSP page writes to a log file.

Logging user accesses in JSP.

Figure 15.1. Logging user accesses in JSP.

You can see the results in the log file, such as jakarta-tomcat-4.0.3logsch15.2002-05-29.txt (where you'd use the current date in this log file's name). If you look in that file, you can find the entry in this log file caused by the user access:

2002-05-30 10:31:50 WebappLoader[/ch15]: Deploying class repositories to work directory D:
Logging user accesses in JSP.	omcatjakarta-tomcat-4.0.3worklocalhostch15
2002-05-30 10:31:50 WebappLoader[/ch15]: Reloading checks are enabled for this Context
2002-05-30 10:31:50 StandardManager[/ch15]: Seeding random number generator class java
Logging user accesses in JSP..security.SecureRandom
2002-05-30 10:31:50 StandardManager[/ch15]: Seeding of random number generator has been
Logging user accesses in JSP. completed
2002-05-30 10:31:50 ContextConfig[/ch15]: Missing application web.xml, using defaults only
2002-05-30 10:31:50 StandardWrapper[/ch15:default]: Loading container servlet default
2002-05-30 10:31:50 default: init
2002-05-30 10:31:50 StandardWrapper[/ch15:invoker]: Loading container servlet invoker
2002-05-30 10:31:50 invoker: init
2002-05-30 10:31:50 jsp: init
2002-05-30 10:32:00 jsp: init
2002-05-30 10:32:00 User access!  User IP: 127.0.0.1 Resource: /ch15/ch15_01.jsp

This gives you access to the log files maintained by Tomcat itself. In this case, the JSP page only stored a little information about the user, but there are many uses for such logs. For example, if an exception occurs when your JSP executes, you might record it in the log file, including what data caused the problem:

try{ 
    .
    .
    .
}
catch (Exception e){
    session.getServletContext().log("An exception occurred: " + e.getMessage());
    session.getServletContext().log("Current parameters: " + param1 + ", " + param2);
}

Java comes with dozens of classes built in to work with files. Most of those classes work by writing data to files or reading it back, but there's one class—the File class—that enables you to work with files without even opening them.

Using the File Class

You can use the File class to handle files in Java without needing to open a file to access its contents. For example, using the File class, you can get a list of the files in a particular directory, delete a file, or get a file's length. This class is a big one—you can see the fields of the File class in Table 15.1, and its methods in Table 15.2.

Table 15.1. Fields of the File Class

Field

Means This

String pathSeparator

The (system-dependent) path-separator character ( in Windows, / in Unix) as a string.

char pathSeparatorChar

The (system-dependent) path-separator character.

String separator

The (system-dependent) default name-separator character, as a string.

char separatorChar

The (system-dependent) default name-separator character.

Table 15.2. Methods of the File Class

Method

Does This

File(File parent, String child)

Creates a new File instance from a parent abstract pathname and a child pathname string.

File(String pathname)

Creates a new File instance by converting the given pathname string into an abstract pathname.

File(String parent, String child)

Creates a new File object using a parent pathname string and a child pathname string.

File(URI uri)

Creates a new File instance by converting the given file URI into a pathname.

boolean canRead()

True if the application can read a file.

boolean canWrite()

True if the application can modify the file.

int compareTo(File pathname)

Compares two pathnames.

int compareTo(Object o)

Compares the pathname to another object.

boolean createNewFile()

Creates a new empty file.

static File createTempFile (String prefix, String suffix)

Creates an empty file in the default temporary-file directory using the specified file prefix and suffix.

static File createTempFile (String prefix, String suffix, and File directory)

Creates an empty file in the specified directory using the specified file prefix and suffix.

boolean delete()

Deletes the file or directory.

void deleteOnExit()

Requests that the file or directory be deleted when the Java terminates.

boolean equals(Object obj)

True if the file matches the specified object.

boolean exists()

True if the file exists.

File getAbsoluteFile()

Returns the absolute form of the pathname.

String getAbsolutePath()

Returns the absolute pathname string of this file.

String getName()

Returns the name of the file or directory.

String getParent()

Returns the pathname string of the file's parent directory.

File getParentFile()

Returns the pathname of the file's parent directory as a File object.

String getPath()

Converts the pathname into a string.

int hashCode()

Computes a hash code for the pathname.

boolean isAbsolute()

True if the pathname is absolute.

boolean isDirectory()

True if the file is actually a directory.

boolean isFile()

True if the file is a normal file.

boolean isHidden()

True if the file is a hidden file.

long lastModified()

Returns the time that the file was last modified.

long length()

Returns the length of the file.

String[] list()

Returns an array of strings naming the files and directories in the directory.

String[] list (FilenameFilter filter)

Returns an array of strings naming the files and directories in the directory given the specified filter.

File[] listFiles()

Returns an array of pathnames naming the files in the directory.

File[] listFiles(FileFilter filter)

Returns an array of pathnames naming the files and directories in the directory given the specified filter.

File[] listFiles (FilenameFilter filter)

Returns an array of pathnames naming the files and directories in the directory given the specified FilenameFilter filter.

static File[] listRoots()

Lists the available file system's roots.

boolean mkdir()

Creates the directory specified by the pathname.

boolean mkdirs()

Creates the directory named by the pathname.

boolean renameTo(File dest)

Renames the file given by the pathname.

boolean setLastModified(long time)

Sets the last-modified time of the file or directory.

boolean setReadOnly()

Marks the file or directory as read-only.

String toString()

Returns the pathname as a string.

URI toURI()

Constructs a file URI that represents this pathname.

URL toURL()

Converts this pathname into a file-type URL.

Here's an example showing how to work with the File class. Say that you wanted to present the user with a list of files he can download. You can get such a list using the File class, and you can make each filename into a hyperlink to the file itself as you display it.

Here's one thing to remember when you work with files—from the server's point of view, the directory it treats as the root directory for your Web application is the directory where your Web application is located. However, from Java's point of view, the root directory is the actual root directory of the disk itself. To get the actual path to your application's directory in terms that Java can understand, you can use the application object's getRealPath method:

String file = application.getRealPath("/"); 

Now you can use this real path to create a new File object, and get an array of the filenames in that directory, and an array of File objects corresponding to those files:

String file = application.getRealPath("/"); 

File file1 = new File(file);
String [] fileNames = file1.list();
File [] fileObjects= file1.listFiles();
        .
        .
        .

To display a hyperlink to each file, as well as each file's length, you can loop over each file like this, making sure it's a real file and not a directory (check with the File class's isDirectory method):

String file = application.getRealPath("/"); 

File file1 = new File(file);
String [] fileNames = file1.list();
File [] fileObjects= file1.listFiles();

for (int loopIndex = 0; loopIndex < fileObjects.length; loopIndex++) {
    if(!fileObjects[loopIndex].isDirectory()){
        .
        .
        .
    }
}

Now it's easy to create a hyperlink to each file in the current directory, as well as to indicate its length in bytes (which you can get with the File class's length method), as you see in Listing 15.2.

Example 15.2. Displaying a List of Downloadable Files (ch15_02.jsp)

<%@ page import="java.io.*" %>
<HTML>
    <HEAD>
        <TITLE>Index of Files</TITLE>
    </HEAD>

    <BODY>
        <H1>Index of Files</H1>
        Click a file to open/download it...
        <%
            String file = application.getRealPath("/");

            File file1 = new File(file);
            String [] fileNames = file1.list();
            File [] fileObjects= file1.listFiles();
        %>
        <UL>
        <%
            for (int loopIndex = 0; loopIndex < fileObjects.length; loopIndex++) {
                if(!fileObjects[loopIndex].isDirectory()){
        %>
        <LI>
          <A HREF="<%= fileNames[loopIndex] %>"><%= fileNames[loopIndex] %></A>
          &nbsp;&nbsp;&nbsp;&nbsp;
          (<%= Long.toString(fileObjects[loopIndex].length()) %> bytes long)
        <%
                }
            }
        %>
        </UL>
    </BODY>
</HTML>

You can see the results in Figure 15.2, where you see a list of hyperlinks giving the names of the files in the current directory (the same directory that ch15_02.jsp is in), as well as the length of each file. All the user has to do to open or download a file is click its name, making this a handy utility application, indeed.

Displaying a downloadable list of files.

Figure 15.2. Displaying a downloadable list of files.

As you can see, the File class is a use ful one. However, to work with the actual contents of a file, you need to use some of the dozens of Java classes targeted to that task, and that's coming up next.

Streams, Readers, and Writers

Streams, Readers, and Writers

In Java, stream I/O is based on the InputStream, OutputStream, Reader, and Writer classes. InputStream and OutputStream are for byte-oriented streams (when you work with binary files), and the Reader and Writer classes are for character-oriented streams (when you work with text files). You don't use these classes directly; instead, you derive classes from them, and in fact, Java has already done a lot of that for you—so much that there are many classes to work with. Here are some of them:

  • Writer subclasses—. BufferedWriter, CharArrayWriter, FilterWriter, OutputStreamWriter, PipedWriter, PrintWriter, StringWriter, FileWriter

  • Reader subclasses—. BufferedReader, CharArrayReader, FilterReader, InputStreamReader, PipedReader, StringReader, FileReader

  • InputStream subclasses—. AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream

  • OutputStream subclasses—. ByteArrayOutputStream, FileOutputStream, FilterOutputStream, ObjectOutputStream, OutputStream, and PipedOutputStream

You use a stream class's constructor to create a stream and open a file. The stream classes are usually divided into input and output streams, so you select the one that's appropriate—input to read, output to write. After you have a stream object, you can use its methods, typically read and write, to transfer data. When you're done with the stream object, you typically use its close method to close it and end your work with the data source or store.

When you work with files today, you'll use the file-oriented streams. In particular, the text files you'll be working with today are FileWriter and FileReader. The binary files you'll be working with today are FileInputStream and FileOutputStream. First on the agenda is learning how to work with text files, so you can see how to support a guest book into which the user can enter text.

Working with Text Files

The work you'll see here on text files uses the FileWriter and FileReader classes. These stream classes inherit methods from other classes in turn. For example, here's how the java.io.FileWriter class you'll be using is derived:

java.lang.Object 
  |
  |__java.io.Writer
        |
        |__java.io.OutputStreamWriter
              |
              |__java.io.FileWriter

You can see the methods of the java.io.Writer class in Table 15.3, the methods of java.io.OutputStreamWriter in Table 15.4, and the methods of the java.io.FileWriter class in Table 15.5.

Table 15.3. Methods of the java.io.Writer Class

Method

Does This

protected Writer()

Creates a new character-stream writer object.

protected Writer(Object lock)

Creates a new character-stream writer object that you can synchronize with other programs using the specified object.

abstract void close()

Closes the stream.

abstract void flush()

Flush the stream, writing all remaining data out.

void write(char[] cbuf)

Writes an array of characters.

abstract void write(char[] cbuf, int off, int len)

Writes a part of an array of characters.

void write(int c)

Writes a single character.

void write(String str)

Writes a string.

void write(String str, int off, int len)

Writes a part of a string.

Table 15.4. Methods of the OutputStreamWriter Class

Method

Does This

OutputStreamWriter(OutputStream out)

Creates an OutputStreamWriter object.

OutputStreamWriter(OutputStream out, String enc)

Creates an OutputStreamWriter object that uses the given character encoding.

void close()

Closes the stream.

void flush()

Flushes the stream.

String getEncoding()

Returns the name of the character encoding being used.

void write(char[] cbuf, int off, int len)

Writes a part of an array of characters.

void write(int c)

Writes a single character.

void write(String str, int off, int len)

Writes a part of a string.

Table 15.5. Methods of the FileWriter Class

Method

Does This

FileWriter(File file)

Constructs a FileWriter object using the specified File object.

FileWriter(File file, boolean append)

Constructs a FileWriter object given a File object.

FileWriter(FileDescriptor fd)

Constructs a FileWriter object for a file descriptor.

FileWriter(String fileName)

Constructs a FileWriter object using a filename.

FileWriter(String fileName, boolean append)

Constructs a FileWriter object given a filename with a Boolean value that indicates whether new data will be appended.

Similarly, you'll be using the FileReader class to read from text files today. Here's the hierarchy of this class:

java.lang.Object 
  |
  |__java.io.Reader
        |
        |__java.io.InputStreamReader
              |
              |__java.io.FileReader

You can find the methods of the Reader class in Table 15.6, the methods of the InputStreamReader class in Table 15.7, and the methods of the FileReader in Table 15.8.

Table 15.6. Methods of the Reader Class

Method

Does This

protected Reader()

Creates a new character-stream reader.

protected Reader(Object lock)

Creates a new character-stream reader synchronized with other processes using the specified object.

abstract void close()

Closes the stream.

void mark(int readAheadLimit)

Marks the current position in the stream.

boolean markSupported()

True if this stream supports the mark method.

int read()

Reads a single character.

int read(char[] cbuf)

Reads characters into an array.

abstract int read(char[] cbuf, int off, int len)

Reads characters into a part of an array.

boolean ready()

Tells whether this stream is ready to be read.

void reset()

Resets the stream.

long skip(long n)

Skips n characters.

Table 15.7. Methods of the InputStreamReader Class

Method

Does This

InputStreamReader(InputStream in)

Creates an InputStreamReader object.

InputStreamReader(InputStream in, Charset cs)

Creates an InputStreamReader object that uses the specified charset.

InputStreamReader(InputStream in, CharsetDecoder dec)

Creates an InputStreamReader object that uses the specified charset decoder.

InputStreamReader(InputStream in, String charsetName)

Creates an InputStreamReader object that uses the named charset.

void close()

Closes the stream.

String getEncoding()

Returns the name of the character encoding being used.

int read()

Reads a single character.

int read(char[] cbuf, int off, int len)

Reads characters into a part of an array.

boolean ready()

True if this stream is ready to be read.

Table 15.8. Methods of the FileReader Class

Method

Does This

FileReader(File file)

Creates a new FileReader object, using a File object.

FileReader(FileDescriptor fd)

Creates a new FileReader object, using a FileDescriptor object.

FileReader(String fileName)

Creates a new FileReader object, using the specified name of the file to read from.

As with everything else in this book, this is all made clearer with an example, and it's time to look at one now.

Writing Text Files

This text file example will let users write to a guest book that's stored on the server. For example, the Web page you see in Listing 15.3 accepts the user's name and a message for the guest book application, which is ch15_04.jsp.

Example 15.3. HTML Page for the Guest Book (ch15_03.html)

<HTML>
    <HEAD>
        <TITLE>Please Sign My Guest Book!</TITLE>
    </HEAD>

    <BODY>
        <CENTER>
            <H1>Please Sign My Guest Book!</H1>
            <FORM ACTION="ch15_04.jsp" METHOD="POST">
                Your name:
                <INPUT TYPE="TEXT" NAME="TEXT1">
                <BR>
                <BR>
                <BR>
                Your comments:
                <BR>
                <TEXTAREA NAME="TEXTAREA1" ROWS="5" COLS="50"></TEXTAREA>
                <BR>
                <INPUT TYPE="SUBMIT" VALUE="Submit"><INPUT TYPE="RESET" VALUE="Reset">
            </FORM>
        </CENTER>
    </BODY>
</HTML>

You can see this Web page at work in Figure 15.3.

Accepting guest book input from the user.

Figure 15.3. Accepting guest book input from the user.

In the JSP page that will handle the guest book (ch15_04.jsp), the code starts by getting the user's name and comments, as well as the real path of the file in which it will store the information (ch15_04.txt):

<% 
    String name = request.getParameter("TEXT1");
    String text = request.getParameter("TEXTAREA1");
    String file = application.getRealPath("/") + "ch15_04.txt";
        .
        .
        .
%>

Next, open the file ch15_04.txt using the FileWriter constructor:

<% 
    String name = request.getParameter("TEXT1");
    String text = request.getParameter("TEXTAREA1");
    String file = application.getRealPath("/") + "ch15_04.txt";

    FileWriter filewriter = new FileWriter(file, true);
        .
        .
        .
%>

The second argument to the FileWriter constructor, true, indicates to FileWriter that you want to append text to the end of ch15_04.txt. (The default is to simply open the file you want to write to and delete its current contents before you start writing.)

Now you can write the data the user sent along with a little HTML in the guest book file, ch15_04.txt, using the write method. When you're done writing, you use the close method to close the stream, which closes the file and makes it available to other applications:

<% 
    String name = request.getParameter("TEXT1");
    String text = request.getParameter("TEXTAREA1");
    String file = application.getRealPath("/") + "ch15_04.txt";

    FileWriter filewriter = new FileWriter(file, true);
    filewriter.write("<B>Name: </B>" + name + "<BR>");
    filewriter.write("<B>Comments: </B><BR>");
    filewriter.write(text + "<BR><BR>");
    filewriter.close();
%>

You can also display the current contents of the guest book simply by including the guest book file, ch15_04.txt, in the output using <jsp:include>, as you see in the whole guest book application, ch15_04.jsp, which appears in Listing 15.4.

Example 15.4. A Guest Book JSP Application (ch15_04.jsp)

<%@ page import="java.io.*" %>
<HTML>
    <HEAD>
        <TITLE>Thanks for Adding to the Guest Book!</TITLE>
    </HEAD>

    <BODY>
        <H1>Thanks for Adding to the Guest Book!</H1>
        Here's what you and others have said:
        <BR>
        <BR>
        <%
            String name = request.getParameter("TEXT1");
            String text = request.getParameter("TEXTAREA1");
            String file = application.getRealPath("/") + "ch15_04.txt";

            FileWriter filewriter = new FileWriter(file, true);
            filewriter.write("<B>Name: </B>" + name + "<BR>");
            filewriter.write("<B>Comments: </B><BR>");
            filewriter.write(text + "<BR><BR>");
            filewriter.close();
        %>
        <jsp:include page="ch15_04.txt" flush="true"/>
    </BODY>
</HTML>

After the user sends her name and comments to the guest book, you can see the result in Figure 15.4, where the JSP application has stored the user's input in the guest book, and displays the current contents of the guest book.

Storing data in the guest book.

Figure 15.4. Storing data in the guest book.

You can also explicitly open the guest book file and read it without using <jsp:include> at all—that's coming up next.

Reading Text Files

To explicitly open the guest book and read it, you can use a FileReader object, which supports a read method. The read method enables you to read text from a text file and store that text in a character array. You can supply the read method with an offset into the file to start reading, and the number of bytes to read. To set how many bytes to read, you can use a File object to get the length of the guest book file.

First, get the real path to the guest book file, ch15_04.txt, and create a new File object using that file:

<% 
    String file = application.getRealPath("/") + "ch15_04.txt";
    File fileObject = new File(file);
        .
        .
        .
%>

Now you can create a character array named data of the same length as the guest book file to hold the guest book data, and create a FileReader object to read the data from that file. The FileReader object's read method will return the number of characters actually read. (You can ask it to read more bytes than there are in the file, and the read method will let you know how many bytes were actually read.) Here's how the code reads the guest book into the array named data:

<% 
    String file = application.getRealPath("/") + "ch15_04.txt";
    File fileObject = new File(file);

    char data[] = new char[(int) fileObject.length()];
    FileReader filereader = new FileReader(file);
    int charsread = filereader.read(data);
        .
        .
        .
%>

All that's left is to display the guest book, and to close the FileReader object, as you see in Listing 15.5.

Example 15.5. Displaying the Guest Book (ch15_05.jsp)

<%@ page import="java.io.*" %>
<HTML>
    <HEAD>
        <TITLE>The Guest Book</TITLE>
    </HEAD>

    <BODY>
        <H1>The Guest Book</H1>
        Here are the current entries in the guest book:
        <BR>
        <BR>
        <%
            String file = application.getRealPath("/") + "ch15_04.txt";
            File fileObject = new File(file);

            char data[] = new char[(int) fileObject.length()];
            FileReader filereader = new FileReader(file);

            int charsread = filereader.read(data);
            out.println(new String(data, 0 , charsread));

            filereader.close();
        %>
    </BODY>
</HTML>

You can see the results in Figure 15.5, which shows the guest book's contents, success fully read and displayed.

Opening the guest book.

Figure 15.5. Opening the guest book.

The FileReader class's read method reads in as much data as you want, but there's another method that's even easier to work with when it comes to text files. The BufferedReader class provides a buffered character reader class that reads data into a temporary buffer before passing it on to you. One of the advantages of this is that BufferedReader provides a readLine method, which is good for reading text files line by line. For example, here's how you read in a file line by line using BufferedReader and a while loop—note that you pass a FileReader object to the BufferedReader class's constructor:

FileReader filereader = new FileReader("file.txt"); 
BufferedReader bufferedreader = new BufferedReader(filereader);
String instring;

while((instring = bufferedreader.readLine()) != null) {
    out.println(instring + "<BR>");
}

filereader.close();

Working with Binary Files

Besides text files, you can also work with binary files in Java. A binary file contains binary data, such as an image file or an executable program. Like text files, you can open, read, write, and close binary files in Java. This is done with binary I/O streams.

When you work with binary files, you can use the FileInputStream class to read from binary files and the FileOutputStream class to write to binary files.

Here's the inheritance diagram of the FileInputStream class:

java.lang.Object 
  |
  |__java.io.InputStream
        |
        |__java.io.FileInputStream

You can see the methods of the InputStream class in Table 15.9, and the methods of the FileInputStream class in Table 15.10.

Table 15.9. Methods of the InputStream Class

Method

Does This

InputStream()

Creates a new InputStream object.

int available()

Returns the number of bytes that can be read from this input stream.

void close()

Closes this input stream.

void mark(int readlimit)

Marks the current position in this input stream.

boolean markSupported()

True if this input stream supports the mark and reset methods.

abstract int read()

Reads the next byte of data from the input stream.

int read(byte[] b)

Reads a number of bytes from the input stream and stores them into a buffer array.

int read(byte[] b, int off, int len)

Reads up to the given number of bytes from the input stream into an array of bytes.

void reset()

Repositions this stream to the position at the time the mark method was last called.

long skip(long n)

Skips over and discards a specified number of bytes of data from this input stream.

Table 15.10. Methods of the FileInputStream Class

Method

Does This

FileInputStream(File file)

Creates a FileInputStream object by opening a connection to the file named by the File object.

FileInputStream(FileDescriptor fdObj)

Creates a FileInputStream object using the file descriptor fdObj.

FileInputStream(String name)

Creates a FileInputStream object by opening a connection to the file named by the pathname.

int available()

Returns the number of bytes that can be read from this file input stream.

void close()

Closes this file input stream.

protected void finalize()

Ensures that the close method of this file input stream is called.

FileChannel getChannel()

Returns the unique FileChannel object associated with this file input stream.

FileDescriptor getFD()

Returns the FileDescriptor object that represents the connection to the actual file.

int read()

Reads a byte of data from this input stream.

int read(byte[] b)

Reads up to b length bytes of data into the specified array of bytes.

int read(byte[] b, int off, int len)

Reads up to len bytes of data from this input stream into an array of bytes.

long skip(long n)

Skips over and discards n bytes of data.

Here's the inheritance diagram for the FileOutputStream class:

java.lang.Object 
  |
  |__java.io.OutputStream
        |
        |__java.io.FileOutputStream

You can see the methods of the OutputStream class in Table 15.11, and the methods of the FileOutputStream class in Table 15.12.

Table 15.11. Methods of the OutputStream Class

Method

Does This

OutputStream()

Creates an OutputStream object.

void close()

Closes this output stream.

void flush()

Flushes this output stream, forcing any buffered output bytes to be written out.

void write(byte[] b)

Writes b length bytes from the specified byte array to this output stream.

void write(byte[] b, int off, int len)

Writes len bytes from the specified byte array starting at the specified offset.

abstract void write(int b)

Writes the specified byte to this output stream.

Table 15.12. Methods of the FileOutputStream Class

Method

Does This

FileOutputStream(File file)

Creates a file output stream object that can write to the file given by the specified File object.

FileOutputStream(File file, boolean append)

Creates a file output stream to write to the file represented by the specified File object.

FileOutputStream(FileDescriptor fdObj)

Creates an output file stream object that can write to the specified file descriptor.

FileOutputStream(String name)

Creates an output file stream to write to the file with the specified name.

FileOutputStream(String name, boolean append)

Creates an output file stream to write to the file with the specified name, and appends to the file if the Boolean value is true.

void close()

Closes this file output stream.

protected void finalize()

Ensures that the close method of this file output stream is called when the stream is done.

FileChannel getChannel()

Returns the FileChannel object associated with this file output stream.

FileDescriptor getFD()

Returns the file descriptor connected to this stream.

void write(byte[] b)

Writes b length bytes from the specified byte array to this file output stream.

void write(byte[] b, int off, int len)

Writes len bytes from the specified byte array starting at the specified offset to this file output stream.

void write(int b)

Writes the given byte to this file output stream.

These classes are best understood by putting them to work—and an example using these classes is coming up next.

Writing Byte Data to Files

Your JSP code might work with binary data, such as an image file—in fact, you'll see how to create image files on the fly in Day 20, “Creating Images On the Fly and Handling Internet Programming.” The data in an image file is binary data, and you typically store such data in byte arrays.

Here's an example showing how to use FileOutputStream to write out a byte array that contains just the values 1, 2, 3, and 4. First, you create a byte array and then a FileOutputStream object connected to a data file:

byte data[] = {1, 2, 3, 4}; 
String file = application.getRealPath("/") + "ch15_06.dat";
FileOutputStream fileoutputstream = new FileOutputStream(file);
        .
        .
        .

Then you can use the write method to write your data byte by byte to the output file:

byte data[] = {1, 2, 3, 4}; 
String file = application.getRealPath("/") + "ch15_06.dat";
FileOutputStream fileoutputstream = new FileOutputStream(file);

for (int loopIndex = 0; loopIndex < data.length; loopIndex++) {
    fileoutputstream.write(data[loopIndex]);
}

You can see this code in a JSP page in Listing 15.6.

Example 15.6. Writing Byte Data (ch15_06.jsp)

<%@ page import="java.io.*" %>
<HTML>
    <HEAD>
        <TITLE>Writing Binary Data</TITLE>
    </HEAD>

    <BODY>
        <H1>Writing Binary Data</H1>
        This page writes binary data to a file.
        <BR>

        <%
            byte data[] = {1, 2, 3, 4};
            String file = application.getRealPath("/") + "ch15_06.dat";
            FileOutputStream fileoutputstream = new FileOutputStream(file);

            for (int loopIndex = 0; loopIndex < data.length; loopIndex++) {
                fileoutputstream.write(data[loopIndex]);
            }

            fileoutputstream.close();
        %>
        Four byte values were written to a file byte by byte.
    </BODY>
</HTML>

You can see this JSP page at work in Figure 15.6.

Writing byte data to a file.

Figure 15.6. Writing byte data to a file.

The next step, of course, is to read the data back in.

Reading Byte Data from Files

To read the byte data you just wrote to file, you can use FileInputStream. First, connect a FileInputStream object to the data file:

String file = application.getRealPath("/") + "ch15_06.dat"; 
FileInputStream fileinputstream = new FileInputStream(file);
        .
        .
        .

Then you can determine how many bytes there are to read with the available method, and read that data into a byte array with the read method:

String file = application.getRealPath("/") + "ch15_06.dat"; 
FileInputStream fileinputstream = new FileInputStream(file);

int numberBytes = fileinputstream.available();
byte bytearray[] = new byte[numberBytes];

fileinputstream.read(bytearray);
        .
        .
        .

Finally, loop over the resulting byte array and display its data, as in Listing 15.7.

Example 15.7. Reading a Binary File (ch15_07.jsp)

<%@ page import="java.io.*" %>
<HTML>
    <HEAD>
        <TITLE>Reading Binary Data</TITLE>
    </HEAD>

    <BODY>
        <H1>Reading Binary Data</H1>
        This page reads binary data from a file.
        <BR>
        Read this data:
        <BR>
        <BR>

        <%
            String file = application.getRealPath("/") + "ch15_06.dat";
            FileInputStream fileinputstream = new FileInputStream(file);

            int numberBytes = fileinputstream.available();
            byte bytearray[] = new byte[numberBytes];

            fileinputstream.read(bytearray);

            for(int loopIndex = 0; loopIndex < numberBytes; loopIndex++){
                out.println(bytearray[loopIndex]);
            }

            fileinputstream.close();
        %>
    </BODY>
</HTML>

You can see the results in Figure 15.7—the byte data was successfully read back in.

Reading byte data from a file.

Figure 15.7. Reading byte data from a file.

That's fine if you want to work with binary data byte by byte—but what if you want to work with objects or primitive data types, such as double values?

Writing Objects to Files

There's a special class you can use to write objects and primitive data types in binary to files—the ObjectOutputStream class. You can see the methods of this class, which include methods such as writeInt and writeDouble to write primitive data types to files, in Table 15.13.

Table 15.13. Methods of the ObjectOutputStream Class

Method

Does This

protected ObjectOutputStream()

A protected constructor.

ObjectOutputStream(OutputStream out)

Creates an ObjectOutputStream object for the specified OutputStream.

protected void annotateClass(Class cl)

Enables class data to be stored in the stream.

protected void annotateProxyClass(Class cl)

This method enables custom data to be stored in the stream along with descriptors for dynamic proxy classes.

void close()

Closes the stream.

void defaultWriteObject()

Writes the fields of the current class to this stream.

protected void drain()

Removes any buffered data in ObjectOutputStream.

protected boolean enableReplaceObject enable)

Enables the stream to replace objects in (boolean he stream.

void flush()

Flushes the stream.

ObjectOutputStream.PutField putFields()

Returns the object used to buffer fields.

protected Object replaceObject(Object obj)

Enables subclasses to substitute one object for another.

void reset()

Disregards the state of any objects already written.

void useProtocolVersion(int version)

Specifies the stream protocol to use when writing the stream.

void write(byte[] buf)

Writes an array of bytes.

void write(byte[] buf, int off, int len)

Writes a subarray of bytes.

void write(int val)

Writes a byte.

void writeBoolean(boolean val)

Writes a Boolean.

void writeByte(int val)

Writes an 8-bit byte.

void writeBytes(String str)

Writes a String as a sequence of bytes.

void writeChar(int val)

Writes a 16-bit character.

void writeChars(String str)

Writes a String as a sequence of characters.

protected void writeClassDescriptor (ObjectStreamClass desc)

Writes the specified class descriptor.

void writeDouble(double val)

Writes a 64-bit double.

void writeFields()

Writes the buffered fields to the stream.

void writeFloat(float val)

Writes a 32-bit float.

void writeInt(int val)

Writes a 32-bit int.

void writeLong(long val)

Writes a 64-bit long.

void writeObject(Object obj)

Writes the specified object to the ObjectOutputStream.

protected void writeObjectOverride (Object obj)

You can use this to override the default writeObject method.

void writeShort(int val)

Writes a 16-bit short.

protected void writeStreamHeader()

Enables subclasses to add their own header to the stream.

void writeUnshared(Object obj)

Writes an “unshared” object to the ObjectOutputStream.

void writeUTF(String str)

Sets the primitive data write of this String.

For example, take a look at Listing 15.8, which writes four double values, 1.234, 2.345, 3.456, 4.567, to a data file using the ObjectOutputStream method writeDouble.

Example 15.8. Writing Doubles to a File (ch15_08.jsp)

<%@ page import="java.io.*" %>
<HTML>
    <HEAD>
        <TITLE>Writing Binary Data</TITLE>
    </HEAD>
    <BODY>
        <H1>Writing Binary Data</H1>
        This page writes binary data to a file.
        <BR>

        <%
            double data[] = {1.234, 2.345, 3.456, 4.567};
            String file = application.getRealPath("/") + "ch15_08.dat";
            FileOutputStream fileoutputstream = new FileOutputStream(file);
            ObjectOutputStream objectStream = new ObjectOutputStream(fileoutputstream);

            for (int loopIndex = 0; loopIndex < data.length; loopIndex++) {
                objectStream.writeDouble(data[loopIndex]);
            }

            objectStream.close();
        %>
        Four double values were written to a file byte by byte.
    </BODY>
</HTML>

You can see this page at work in Figure 15.8.

Writing double values to a file.

Figure 15.8. Writing double values to a file.

Now that you've written them out, the next step is to read those four double values back in.

Reading Objects from Files

To read the double values back in, you can use the ObjectInputStream class, which includes methods such as readDouble, to read primitive data types back in. You can see the methods of this class in Table 15.14.

Table 15.14. Methods of the ObjectInputStream Class

Method

Does This

protected ObjectInputStream()

A protected constructor.

ObjectInputStream(InputStream in)

Creates an ObjectInputStream that reads from the specified InputStream.

int available()

Returns the number of bytes that can be read.

void close()

Closes the input stream.

void defaultReadObject()

Reads the fields of the current class from the stream.

protected boolean enableResolveObject(boolean enable)

Enables objects to read from the stream to be replaced.

int read()

Reads a byte of data.

int read(byte[] buf, int off, int len)

Reads into an array of bytes.

boolean readBoolean()

Reads in a Boolean.

byte readByte()

Reads an 8-bit byte.

char readChar()

Reads a 16-bit char.

protected ObjectStreamClass readClassDescriptor()

Reads a class descriptor.

double readDouble()

Reads a 64-bit double.

ObjectInputStream.GetField readFields()

Reads the persistent fields from the stream.

float readFloat()

Reads a 32-bit float.

void readFully(byte[] buf)

Reads bytes.

void readFully(byte[] buf, int off, int len)

Reads len bytes.

int readInt()

Reads a 32-bit int.

long readLong()

Reads a 64-bit long.

Object readObject()

Reads an object.

protected Object readObjectOverride()

Overrides the readObject.

short readShort()

Reads a 16-bit short.

protected void readStreamHeader()

Enable subclasses to read and verify their stream headers.

Object readUnshared()

Reads an “unshared” object from the ObjectInputStream.

int readUnsignedByte()

Reads an unsigned 8-bit byte.

int readUnsignedShort()

Reads an unsigned 16-bit short.

String readUTF()

Reads a UTF format String.

void registerValidation(ObjectInputValidation obj, int prio)

Registers an object to be validated.

protected Class resolveClass(ObjectStreamClass desc)

Loads the local equivalent of the specified stream description.

protected Object resolveObject(Object obj)

Enables subclasses to substitute one object for another.

protected Class resolveProxyClass(String[] interfaces)

Returns a proxy class.

int skipBytes(int len)

Skips len bytes.

For example, you can see a JSP page that reads the double values you wrote to disk in Listing 15.9, using the ObjectInputStream method readDouble.

Example 15.9. Reading Doubles From a File (ch15_09.jsp)

<%@ page import="java.io.*" %>
<HTML>
    <HEAD>
        <TITLE>Reading Binary Data</TITLE>
    </HEAD>

    <BODY>
        <H1>Reading Binary Data</H1>
        This page reads binary data from a file.
        <BR>
        Read this data:
        <BR>
        <BR>

        <%
            String file = application.getRealPath("/") + "ch15_08.dat";
            FileInputStream fileinputstream = new FileInputStream(file);
                   ObjectInputStream objectStream = new ObjectInputStream(fileinputstream);

            for(int loopIndex = 0; loopIndex < 4; loopIndex++){
                double d = objectStream.readDouble();
                out.println(d);
            }

            objectStream.close();
        %>
    </BODY>
</HTML>

You can see the results of this code in Figure 15.9, which shows that all the double values were read back in successfully.

Reading double data from a file.

Figure 15.9. Reading double data from a file.

That completes our work with files on the server for today. You've gotten a good introduction to the topic, but bear in mind that file handling in Java is a large and complex topic—for more details, see the Java documentation, or look up books on Java.

Summary

Today you were introduced to the topic of creating files on the server, starting with Tomcat's own built-in capability to create log files that you can access. In particular, you learned how to name and write to such log files.

You also learned about the File class, which enables you to work with files on the server. You learned how to get a list of filenames and File objects for the files in a directory, as well as methods that let you delete and rename files.

You also got an introduction to working with the contents of files on the server using Java streams. You can use streams to read and write data to files on the server using Java, and there are many stream classes to choose from.

Finally, you learned how to use the main streams that deal with files in Java: the FileReader and FileWriter classes for text files, and the FileInputStream and FileOutputStream to work with binary files.

Tomorrow you'll see more on working with a particular type of file—database files. One of the most important uses of JSP on Web servers is to interact with databases, and you're going to see how that works over the next two days.

Q&A

Q1:

Is there some way to move around in a file when it has been opened?

A1:

Using the RandomAccessFile class, you can move around in a file using the seek method. (That's what random access is all about—being able to move around in a file at random.)

Q2:

Besides file streams, what other kinds of streams are there in Java?

A2:

There are many—for example, the ByteArrayReader class reads byte data from an array, and the ByteArrayWriter class writes data to a byte array. The CharArrayReader class reads character data from an array, and the CharArrayWriter class writes data to a character array.

Workshop

This workshop tests whether you understand all the concepts you learned today. It's a good idea to master today's concepts by honing your knowledge here before starting tomorrow's material. You can find the answers to the quiz questions in Appendix A.

Quiz

1:

What method can you use to delete a file?

2:

How can you check whether a file exists?

3:

How do you get a file's true path—the one Java can use?

4:

How do you indicate to the FileWriter stream that you want to append data to a file, not overwrite it?

5:

Name two stream classes you can use with the content of binary files.

Exercises

1:

Modify today's guest book example to display the date and time each entry was made.

2:

Create an example that writes out an array of int, float, and double values, and then reads them back in successfully.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.79.241