|
![]() |
![]() |
![]() |
![]() |
You probably don't need a file copy program as your operating system is bound to provide a facility for this. However, it is a useful way of demonstrating how a file channel for any input file can transfer data directly to a file channel for an output file without involving explicit buffers.
A file channel defines two methods for direct data transfer: :
A channel that was obtained from a FileInputStream object will only support the transferTo() method. Similarly, a channel that was obtained from a FileOutputStream object will only support the transferFrom() method. Both of these methods can throw any of the following flurry of exceptions:
The value of these methods lies in the potential for using the I/O capabilities of the underlying operating system directly. Where this is possible, the operation is likely to be much faster than copying from one file to another in a loop using the read() and write() methods we have seen.
A file copy program is an obvious candidate for trying out these methods.
We will put together a program that will copy the file that is specified by a command line argument. We will copy the file to a backup file that we will create in the same directory as the original. We will create the name of the new file by appending "_backup" to the original file name as many times as necessary to form a unique file name. That operation is a good candidate for writing a helper method:
// Method to create a unique backup File object public static File createBackupFile(File aFile) { aFile = aFile.getAbsoluteFile(); // Ensure we have an absolute path File parentDir = new File(aFile.getParent()); // Get the parent directory String name = aFile.getName(); // Get the file name int period = name.indexOf('.'); // Find the extension separator if(period == -1) // If there isn't one period = name.length(); // set it to the end of the string String nameAdd = "_backup"; // String to be appended // Create a File object that is unique File backup = new File(name.substring(0,period) + nameAdd + name.substring(period)); while(backup.exists()) { // If the name already exists... name = backup.getName(); // Get the current name of the file period += nameAdd.length(); // Increment separator index backup = new File(parentDir,name.substring(0,period) // add _backup again + nameAdd + name.substring(period)); } return backup; }
This method assumes the argument has already been validated as a real file. After making sure that aFile is not a relative path we extract the basic information we need to create the new file – the parent directory, the file name, and where the period separator is, if there is one. We then create a File object using the original file name with "_backup" appended. The while loop will execute as long as the name already exists as a file, and will append further instances of "_backup" until a unique file name is arrived at.
We can now write the method main() to use this method to create the destination file for the file copy operation:
import java.io.*; import java.nio.*; import java.nio.channels.FileChannel; public class FileCopy { public static void main(String[] args) { if(args.length==0) { System.out.println("No file to copy. Application usage is:\n"+ "java -classpath . FileCopy \"filepath\"" ); System.exit(1); } File fromFile = new File(args[0]); if(!fromFile.exists()) { System.out.println("File to copy, "+fromFile.getAbsolutePath() + ", does not exist."); System.exit(1); } File toFile = createBackupFile(fromFile); FileInputStream inFile = null; FileOutputStream outFile = null; try { inFile = new FileInputStream(fromFile); outFile = new FileOutputStream(toFile); } catch(FileNotFoundException e) { e.printStackTrace(System.err); assert false; } FileChannel inChannel = inFile.getChannel(); FileChannel outChannel = outFile.getChannel(); try { int bytesWritten = 0; long byteCount = inChannel.size(); while(bytesWritten<byteCount) bytesWritten += inChannel.transferTo(bytesWritten, byteCount-bytesWritten, outChannel); System.out.println("File copy complete. " + byteCount + " bytes copied to " + toFile.getAbsolutePath()); inFile.close(); outFile.close(); } catch(IOException e) { e.printStackTrace(System.err); System.exit(1); } System.exit(0); } // Code for createBackupFile() goes here... }
You could try this out by copying the source for the program using the command:
java FileCopy FileCopy.java
You should get output something like:
File copy complete. 3036 bytes copied to D:\Beg Java 1.4\Examples\FileCopy_backup.java
Of course, if the source file layout is different or you have a few more – or less – comments, the number of bytes copied will be different. Also the file path will be your path, not mine. In any event, you should be able to check that the new file's contents are identical to the original.
How It Works
We first obtain the command line argument and create a File object from it with the code:
if(args.length==0) { System.out.println("No file to copy. Application usage is:\n"+ "java -classpath . FileCopy \"filepath\"" ); System.exit(1); } File fromFile = new File(args[0]);
If there's no command line argument we supply a message explaining how to use the program.
Next we verify that this is a real file:
if(!fromFile.exists()) { System.out.println("File to copy, "+fromFile.getAbsolutePath() + ", does not exist."); System.exit(1); }
If it isn't, there's nothing we can do, so we bail out of the program.
Creating a File object for the backup file is a piece of cake:
File toFile = createBackupFile(fromFile);
We saw how this method works earlier.
We now create a pair of file stream objects to work with:
FileInputStream inFile = null; FileOutputStream outFile = null; try { inFile = new FileInputStream(fromFile); outFile = new FileOutputStream(toFile); } catch(FileNotFoundException e) { e.printStackTrace(System.err); assert false; }
Since we checked the File objects, we know we won't see a FileNotFoundException being thrown but we still must provide for the possibility. Of course, the FileInputStream object corresponds to the file name entered on the command line. Creating the FileOutputStream object will result in a new empty file being created, ready for loading with the data from the input file.
Next we get the channel for each file from the file streams:
FileChannel inChannel = inFile.getChannel(); FileChannel outChannel = outFile.getChannel();
Once we have the channel objects we transfer the contents of the input file to the output file like this:
try { int bytesWritten = 0; long byteCount = inChannel.size(); while(bytesWritten<byteCount) bytesWritten += inChannel.transferTo(bytesWritten, byteCount-bytesWritten, outChannel); System.out.println("File copy complete. " + byteCount + " bytes copied to " + toFile.getAbsolutePath()); inFile.close(); outFile.close(); } catch(IOException e) { e.printStackTrace(System.err); System.exit(1); }
The data is copied using the transferTo() method for inChannel. You could equally well use the transferFrom() method for outChannel. The chances are the transferTo() method will transfer all the data in one go. The while loop is there just in case it doesn't. The loop condition checks whether the number of bytes written is less than the number of bytes in the file. If it is, the loop executes another transfer operation for the number of bytes left in the file with the file position specified as the number of bytes written so far.
![]() |
![]() |
![]() |