2013-12-09 8 views
3

Я работаю над процессом Java, который должен эффективно (и рекурсивно) копировать файлы/каталоги из исходного местоположения в место назначения.Копирование файлов с файловыми замками в Java

Чтобы сделать это, я хочу:

  • создать замок
  • , если файл назначения не существует, скопируйте его
  • иначе, если файл назначения отличается, скопируйте его
  • иначе они одинаковы, так что ничего не делать
  • расцепления блокировки

Чтобы проверить содержимое, я планировал использовать метод Apache Commons IO FileUtilscontentsEqual(...). Чтобы сделать копирование, я планировал использовать метод Apache Commons IO FileUtilscopyFile(...).

Таким образом, код, который я придумал это (это только для файлов, каталогов обрабатываются рекурсивно до этого метода для файлов):

private static void checkAndUpdateFile(File src, File dest) throws IOException { 
    FileOutputStream out = new FileOutputStream(dest); 
    FileChannel channel = out.getChannel(); 
    FileLock lock = channel.lock(); 

    if (!dest.exists()) { 
    FileUtils.copyFile(src, out); 
    } else if (!FileUtils.contentEquals(src, dest)) { 
    FileUtils.copyFile(src, out); 
    } 

    lock.release(); 
    channel.close(); 
    out.close(); 
} 

Это файл блокировки (большой), а также копии файлы (супер).

Однако, всякий раз, когда он копирует файлы, он устанавливает временную метку скопированных файлов на время копирования. Это означает, что последующие вызовы FileUtils.contentEquals(src, dest) продолжают возвращать false, поэтому файлы переупорядочиваются.

Что мне очень нравится, это похоже на FileUtils.copyFile(src, dest, true), который сохраняет временные метки файла - и это правда, если вы звоните FileUtils.contentEquals(src, dest). Это потребует блокировки на File, а не FileOutputStream, поскольку в противном случае вызов FileUtils.copyFile(src, dest, true) завершается с ошибкой и выдает исключение, потому что файл заблокирован.

В качестве альтернативы, я рассмотрел возможность выполнения метода FileUtils.copyFile(src, dest, true), который должен назвать dest.setLastModified(src.lastModified()). Однако это нужно будет вызывать после освобождения блокировки, что может вызвать проблемы, если один и тот же процесс был выполнен несколько раз одновременно.

Я также рассмотрел ид поставить замок на исходный файл, но это не помогает, как я должен поставить его на FileInputStream, и я хочу передать File в FileUtils.copyFile(src, dest).

Итак:

  1. Есть ли более простой способ добиться того, что я пытаюсь сделать?
  2. Можно ли поместить замок в файл, а не в производную от файла?
  3. Что было бы лучшим способом разрешить это?
    • i.e. Мне просто нужно написать собственный метод подхода для части этого? (Вероятно copyFile(...))

ответ

0

Итак ... в конце концов, я пошел за подход написания своих собственных copy() и compare() методы использовать FileChannel объекты, которые были заблокированы. Ниже приведено решение, которое я придумал, хотя я ожидаю, что другие могут предложить улучшения. Об этом сообщается, посмотрев исходный код для классов apache.commons.io FileUtils и IOUtils.

private static final int s_eof = -1; 
private static final int s_byteBuffer = 10240; 

private static void checkAndUpdateFile(File src, File dest) throws IOException { 
    FileInputStream in = new FileInputStream(src); 
    FileChannel srcChannel = in.getChannel(); 
    FileChannel destChannel = null; 
    FileLock destLock = null; 

    try { 
    if (!dest.exists()) { 
     final RandomAccessFile destFile = new RandomAccessFile(dest, "rw"); 
     destChannel = destFile.getChannel(); 
     destLock = destChannel.lock(); 
     copyFileChannels(srcChannel, destChannel); 
     dest.setLastModified(src.lastModified()); 
    } else { 
     final RandomAccessFile destFile = new RandomAccessFile(dest, "rw"); 
     destChannel = destFile.getChannel(); 
     destLock = destChannel.lock(); 
     if (!compareFileChannels(srcChannel, destChannel)) { 
     copyFileChannels(srcChannel, destChannel); 
     dest.setLastModified(src.lastModified()); 
     } 
    } 
    } finally { 
    if (destLock != null) { 
     destLock.release(); 
    } 
    if (destChannel != null) { 
     destChannel.close(); 
    } 
    srcChannel.close(); 
    in.close(); 
    } 
} 

protected static void copyFileChannels(FileChannel src, 
             FileChannel dest) throws IOException { 
    final long size = src.size(); 
    for (long pos = 0; pos < size;) { 
    long count = 
     ((size - pos) > s_byteBuffer) ? s_byteBuffer : (size - pos); 
    pos += dest.transferFrom(src, pos, count); 
    } 
} 

protected static boolean compareFileChannels(FileChannel a, 
              FileChannel b) throws IOException { 
    if (a.size() != b.size()) { 
    return false; 
    } else { 
    final ByteBuffer aBuffer = ByteBuffer.allocate(s_byteBuffer); 
    final ByteBuffer bBuffer = ByteBuffer.allocate(s_byteBuffer); 
    for (int aCh = a.read(aBuffer); s_eof != aCh;) { 
     int bCh = b.read(bBuffer); 
     if (aCh != bCh || aBuffer.compareTo(bBuffer) != 0) { 
     return false; 
     } 
     aBuffer.clear(); 
     aCh = a.read(aBuffer); 
     bBuffer.clear(); 
    } 
    return s_eof == b.read(bBuffer); 
    } 
} 
0

Вы можете использовать Guava Google Core Library для сравнения двух файлов.

As Byte Source

ByteSource Doc

 ByteSource inByte = Resources.asByteSource(srcFileURL); 
     ByteSource outByte = Files.asByteSource(srcFileURL2); 
     boolean fileEquals= inByte.contentEquals(outByte)); 
+0

Это хорошая идея, но как это работает с блокировкой файлов? Я не могу получить 'FileLock' на' ByteSource', я не могу создать ByteSource из файла, пока есть отдельный замок, и было бы бессмысленно делать «ByteSource» вне блокировки - то есть с точки зрения проблемы , это функционально эквивалентно использованию 'apache.commons.io.FileUtils' – amaidment

+0

@amaidment, вы можете получить URL-адрес из файла http://docs.oracle.com/javase/7/docs/api/java/io/File.html # toURL% 28% 29. Как только вы заблокируете файл, получите URL-адрес и получите исходный источник байта и тот же файл назначения для – Makky

+0

Нет, это не сработает. Когда код вызывает 'inByte.contentEquals (outByte)', он пытается получить доступ к файлу, который был заблокирован, и с ошибкой «IOException: процесс не может получить доступ к файлу, потому что другой процесс имеет заблокированную часть файла», – amaidment