bit-question
bit-question

Reputation: 3843

copy file with stream

The following example shows how to copy file using streams.

private void copyWithStreams(File aSourceFile, File aTargetFile, boolean aAppend) {
log("Copying files with streams.");
ensureTargetDirectoryExists(aTargetFile.getParentFile());
InputStream inStream = null;
OutputStream outStream = null;
try{
  try {
    byte[] bucket = new byte[32*1024];
    inStream = new BufferedInputStream(new FileInputStream(aSourceFile));
    outStream = new BufferedOutputStream(new FileOutputStream(aTargetFile, aAppend));
    int bytesRead = 0;
    while(bytesRead != -1){
      bytesRead = inStream.read(bucket); //-1, 0, or more
      if(bytesRead > 0){
        outStream.write(bucket, 0, bytesRead);
      }
    }
  }
  finally {
    if (inStream != null) inStream.close();
    if (outStream != null) outStream.close();
  }
}
catch (FileNotFoundException ex){
  log("File not found: " + ex);
}
catch (IOException ex){
  log(ex);
}
}

private void ensureTargetDirectoryExists(File aTargetDir){
if(!aTargetDir.exists()){
  aTargetDir.mkdirs();
}
}
private static void log(Object aThing){
  System.out.println(String.valueOf(aThing));
}

For the above code snippet, I feel confused about four points:

1) Bucket is allocated as byte[] bucket = new byte[32*1024]; Is there any criterion for choosing the size, such as 32*1024

2) Why it has to “catch” here? Is there any rule for including catch in writing program?

3) I am also not very clear about the usage of “try” here. It seems the author uses nested try in this program.

Upvotes: 0

Views: 1621

Answers (3)

oesgalha
oesgalha

Reputation: 178

1) There isn't a criterion to define the size of your byte array. In the given code it's used 32 kbytes but you could use any value. The size of the byte array and the size of your file determinate how many times you'll read the file, bigger buffers results in less reading calls, but aren't necessary if you're working with small files.

2) Whenever you use an method in Java that can throws an exception, you need to catch that exception and do something with it. You can treat the exception in your code (usually print the stack trace to debugging purposes) or throw it, which means that when someone use your code will need to catch your code's exception.

3) What he did was to catch the two possible exceptions and specifies which one occurs. Since FileNotFoundException extends IOException he could simply use one try, and catch only the IOException, he coded that way to know if the IOException is a FileNotFoundException or any other IOException. Personally, I wouldn't write the code the same way the author did, it isn't easily readable.

Maybe rewriting the code it gets easier to you understand the tries and catchs:

        System.out.println("Copying files with streams.");
    InputStream inStream = null;
    OutputStream outStream = null;
    try {
        inStream = new BufferedInputStream(new FileInputStream(aSourceFile));
        outStream = new BufferedOutputStream(new FileOutputStream(aTargetFile, aAppend));
    } catch (FileNotFoundException ex){
        // TODO Handle FileNotFoundException
        ex.printStackTrace();
    }
    try {
        byte[] bucket = new byte[32*1024];
        int bytesRead = 0;
        while(bytesRead != -1){
          bytesRead = inStream.read(bucket); //-1, 0, or more
          outStream.write(bucket, 0, bytesRead);
        }
    } catch (IOException ex){
        // TODO Handle IOException
        ex.printStackTrace();
    } finally {
        try {
            if (inStream != null) inStream.close();
            if (outStream != null) outStream.close();
        } catch (IOException ex){
            // TODO Handle IOException
            ex.printStackTrace();
        }
    }

It does the same stuff.

Upvotes: 0

pimaster
pimaster

Reputation: 1967

1) 32 * 1024 comes out to 32 kilobytes which I think is the default allocation unit size when you format an NTFS volume. By matching the underlying file system there are probably some performance gains because you are no re-writing sectors of the disk if it is not caching properly. The bigger the buffer, generally the smoother the operation. You can't use huge buffers on smaller devices though (Phones are coming with more and more memory though)

2) The catch here could be very annoying. If you ask for the file to be copied and the original file doesn't exist, all you get is a log and the program that calls this method might assume that it worked. This method could re-throw the exception and the calling program would have deal with the file not being found. Same for IO issues.

3) The finally is important because you want to ensure the resources are closed no matter what happens. There is really no run time benefit of having the nested try statements as having the resources closed after logging other exceptions. (Minor point, if there is an exception throw whilst closing the inStream, the outStream will not be closed and any other exception would be lost). The programmer might have thought it looked cleaner to put the close statements near the reading and writing but they could very well be moved to the outer try

Upvotes: 1

user684934
user684934

Reputation:

  1. We are assuming that you can't fit the whole file into memory comfortably, so we choose a small chunk at a time, process it, then grab the next chunk. In this case, we're using 32 kilobytes. You could use more or less, but working in (small) multiples of the hard drive sector size (typically 4kb) will be more efficient and result in fewer IO operations.

  2. If you're throwing an exception, or calling a method that throws an exception, you must handle it. You can surround it with a try{} and catch{} (or finally{}) statement block, or you can throw the exception upwards, to whatever method called this one. In this case, you have a try{} statement, so you have the accompanying catch and finally statements.

  3. The author did something unnecessary: he could have used a single try{}, then placed the finally{} block after the catch{} statements. Instead, the inner try{} passes the exception to the outer try{} after executing the finally{} block. After which the catch{} blocks are activated, depending on what type of Exception was thrown.

Upvotes: 1

Related Questions