Java Cookbook, Second Edition

Problem

You need to copy a file in its entirety.

Solution

Use a pair of Streams for binary data, or a Reader and a Writer for text, and a while loop to copy until end-of-file is reached on the input.

Discussion

This operation is fairly common, so I've packaged it as a set of methods in a class called FileIO in my utilities package com.darwinsys.util. Here's a simple test program that uses it to copy a source file to a backup file:

package regress; // in javasrc/darwinsys/src import com.darwinsys.util.FileIO; import java.io.*; public class FileIOTest { public static void main(String[] av) { try { FileIO.copyFile("FileIO.java", "FileIO.bak"); FileIO.copyFile("FileIO.class", "FileIO-class.bak"); } catch (FileNotFoundException e) { System.err.println(e); } catch (IOException e) { System.err.println(e); } } }

How does FileIO work? Its copyFile method takes several forms, depending on whether you have two filenames, a filename and a PrintWriter, and so on. The code for FileIO itself is shown in Example 10-5.

Example 10-5. FileIO.java

package com.darwinsys.io; import java.io.*; /** * Some simple file IO primitives reimplemented in Java. * All methods are static since there is no state. */ public class FileIO { /** Copy a file from one filename to another */ public static void copyFile(String inName, String outName) throws FileNotFoundException, IOException { BufferedInputStream is = new BufferedInputStream(new FileInputStream(inName)); BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(outName)); copyFile(is, os, true); } /** Copy a file from an opened InputStream to an opened OutputStream */ public static void copyFile(InputStream is, OutputStream os, boolean close) throws IOException { int b; // the byte read from the file while ((b = is.read( )) != -1) { os.write(b); } is.close( ); if (close) os.close( ); } /** Copy a file from an opened Reader to an opened Writer */ public static void copyFile(Reader is, Writer os, boolean close) throws IOException { int b; // the byte read from the file while ((b = is.read( )) != -1) { os.write(b); } is.close( ); if (close) os.close( ); } /** Copy a file from a filename to a PrintWriter. */ public static void copyFile(String inName, PrintWriter pw, boolean close) throws FileNotFoundException, IOException { BufferedReader ir = new BufferedReader(new FileReader(inName)); copyFile(ir, pw, close); } /** Open a file and read the first line from it. */ public static String readLine(String inName) throws FileNotFoundException, IOException { BufferedReader is = new BufferedReader(new FileReader(inName)); String line = null; line = is.readLine( ); is.close( ); return line; } /** The size of blocking to use */ protected static final int BLKSIZ = 8192; /** Copy a data file from one filename to another, alternate method. * As the name suggests, use my own buffer instead of letting * the BufferedReader allocate and use the buffer. */ public void copyFileBuffered(String inName, String outName) throws FileNotFoundException, IOException { InputStream is = new FileInputStream(inName); OutputStream os = new FileOutputStream(outName); int count = 0; // the byte count byte[] b = new byte[BLKSIZ]; // the bytes read from the file while ((count = is.read(b)) != -1) { os.write(b, 0, count); } is.close( ); os.close( ); } /** Read the entire content of a Reader into a String */ public static String readerToString(Reader is) throws IOException { StringBuffer sb = new StringBuffer( ); char[] b = new char[BLKSIZ]; int n; // Read a block. If it gets any chars, append them. while ((n = is.read(b)) > 0) { sb.append(b, 0, n); } // Only construct the String object once, here. return sb.toString( ); } /** Read the content of a Stream into a String */ public static String inputStreamToString(InputStream is) throws IOException { return readerToString(new InputStreamReader(is)); } }

A test main program included in the online source copies the source and class files of this program. When I ran it for testing, I followed up by using diff (a text file compare program) on the text file and its backup, and cmp (a binary compare program) on the class files. Both of these programs operate on the Unix "no news is good news" principle: if they say nothing, it is because they found nothing of significance to report i.e., no differences:

C:\javasrc\io>java IOUtil C:\javasrc\io>diff IOUtil.java IOUtil-java.bak C:\javasrc\io>cmp IOUtil.class IOUtil-class.bak C:\javasrc\io>

But wait! Did you look closely at the body of copyFile(String inName, PrintWriter pw, boolean close)? If you didn't, have a look. You'll notice that I cheated and just delegated the work to copyFile(Reader is, Writer os, boolean close). If I'm copying a file from one place on disk to another, why go through the overhead of converting it from external form to Unicode and back? Normally, you won't have to. But if you have something like a network filesystem mounted from Windows to Unix, or vice versa, it's better to do it a line at a time.

Категории