Utility class for synchronizing files/directories : File Utilities « File « Java Tutorial






//$Id: FileHelper.java 15522 2008-11-05 20:06:43Z hardy.ferentschik $

//Revised from hibernate search util
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;


/**
 * Utility class for synchronizing files/directories.
 *
 * @author Emmanuel Bernard
 * @author Sanne Grinovero
 * @author Hardy Ferentschik
 */
public abstract class FileHelper {

  private static final int FAT_PRECISION = 2000;
  public static final long DEFAULT_COPY_BUFFER_SIZE = 16 * 1024 * 1024; // 16 MB


  public static boolean areInSync(File source, File destination) throws IOException {
    if ( source.isDirectory() ) {
      if ( !destination.exists() ) {
        return false;
      }
      else if ( !destination.isDirectory() ) {
        throw new IOException(
            "Source and Destination not of the same type:"
                + source.getCanonicalPath() + " , " + destination.getCanonicalPath()
        );
      }
      String[] sources = source.list();
      Set<String> srcNames = new HashSet<String>( Arrays.asList( sources ) );
      String[] dests = destination.list();

      // check for files in destination and not in source
      for ( String fileName : dests ) {
        if ( !srcNames.contains( fileName ) ) {
          return false;
        }
      }

      boolean inSync = true;
      for ( String fileName : sources ) {
        File srcFile = new File( source, fileName );
        File destFile = new File( destination, fileName );
        if ( !areInSync( srcFile, destFile ) ) {
          inSync = false;
          break;
        }
      }
      return inSync;
    }
    else {
      if ( destination.exists() && destination.isFile() ) {
        long sts = source.lastModified() / FAT_PRECISION;
        long dts = destination.lastModified() / FAT_PRECISION;
        return sts == dts;
      }
      else {
        return false;
      }
    }
  }

  public static void synchronize(File source, File destination, boolean smart) throws IOException {
    synchronize( source, destination, smart, DEFAULT_COPY_BUFFER_SIZE );
  }

  public static void synchronize(File source, File destination, boolean smart, long chunkSize) throws IOException {
    if ( chunkSize <= 0 ) {
      System.out.println("Chunk size must be positive: using default value." );
      chunkSize = DEFAULT_COPY_BUFFER_SIZE;
    }
    if ( source.isDirectory() ) {
      if ( !destination.exists() ) {
        if ( !destination.mkdirs() ) {
          throw new IOException( "Could not create path " + destination );
        }
      }
      else if ( !destination.isDirectory() ) {
        throw new IOException(
            "Source and Destination not of the same type:"
                + source.getCanonicalPath() + " , " + destination.getCanonicalPath()
        );
      }
      String[] sources = source.list();
      Set<String> srcNames = new HashSet<String>( Arrays.asList( sources ) );
      String[] dests = destination.list();

      //delete files not present in source
      for ( String fileName : dests ) {
        if ( !srcNames.contains( fileName ) ) {
          delete( new File( destination, fileName ) );
        }
      }
      //copy each file from source
      for ( String fileName : sources ) {
        File srcFile = new File( source, fileName );
        File destFile = new File( destination, fileName );
        synchronize( srcFile, destFile, smart, chunkSize );
      }
    }
    else {
      if ( destination.exists() && destination.isDirectory() ) {
        delete( destination );
      }
      if ( destination.exists() ) {
        long sts = source.lastModified() / FAT_PRECISION;
        long dts = destination.lastModified() / FAT_PRECISION;
        //do not copy if smart and same timestamp and same length
        if ( !smart || sts == 0 || sts != dts || source.length() != destination.length() ) {
          copyFile( source, destination, chunkSize );
        }
      }
      else {
        copyFile( source, destination, chunkSize );
      }
    }
  }

  private static void copyFile(File srcFile, File destFile, long chunkSize) throws IOException {
    FileInputStream is = null;
    FileOutputStream os = null;
    try {
      is = new FileInputStream( srcFile );
      FileChannel iChannel = is.getChannel();
      os = new FileOutputStream( destFile, false );
      FileChannel oChannel = os.getChannel();
      long doneBytes = 0L;
      long todoBytes = srcFile.length();
      while ( todoBytes != 0L ) {
        long iterationBytes = Math.min( todoBytes, chunkSize );
        long transferredLength = oChannel.transferFrom( iChannel, doneBytes, iterationBytes );
        if ( iterationBytes != transferredLength ) {
          throw new IOException(
              "Error during file transfer: expected "
                  + iterationBytes + " bytes, only " + transferredLength + " bytes copied."
          );
        }
        doneBytes += transferredLength;
        todoBytes -= transferredLength;
      }
    }
    finally {
      if ( is != null ) {
        is.close();
      }
      if ( os != null ) {
        os.close();
      }
    }
    boolean successTimestampOp = destFile.setLastModified( srcFile.lastModified() );
    if ( !successTimestampOp ) {
      System.out.println("Could not change timestamp for {}. Index synchronization may be slow. " + destFile );
    }
  }

  public static void delete(File file) {
    if ( file.isDirectory() ) {
      for ( File subFile : file.listFiles() ) {
        delete( subFile );
      }
    }
    if ( file.exists() ) {
      if ( !file.delete() ) {
        System.out.println( "Could not delete {}" + file );
      }
    }
  }
}








11.55.File Utilities
11.55.1.Ensuring a File Exists
11.55.2.Avoiding Overwriting a File
11.55.3.Copying Files using FileChannel
11.55.4.Format Size
11.55.5.Move File
11.55.6.Compare binary files
11.55.7.Get file date and time
11.55.8.Rename To Temporary Name
11.55.9.Return readable file size with selected value measure
11.55.10.Utility class for synchronizing files/directories
11.55.11.Count files in a directory (including files in all subdirectories)
11.55.12.Extract File Extension
11.55.13.Strip File Extension
11.55.14.Remove File Name Suffix
11.55.15.Get File Name Suffix