2016-01-14 8 views
0

У меня есть две структуры каталогов и их необходимо синхронизировать, т. Е. Скопировать в целевую папку файлы и папки из исходной папки, которые являются новыми или измененными, и удалять файлы и папки в целевом объекте, которых нет в источнике. Тем не менее, для этого процесса требуется кнопка «Отмена», которая не позволит ей выполнить и отменить все изменения. Я обыскал и нашел этот код, но мое понимание того, как оно работает, в лучшем случае является туманным.Как реализовать синхронизацию стоп-папок и отмену в java?

//$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); 
     } 
    } 
    } 
} 

Кодовые функционирует как рекламируется, но я не уверен о том:

  1. Как бы осуществить отмену? Лучшее, что я могу придумать, - это ввести логическую переменную synchronizedCancelled, которая будет установлена ​​в true при нажатии кнопки отмены и синхронизации шнуровки() с проверками, которые не позволят ему выполнить, если false.

  2. Как осуществить откат? Лучшая идея, которую я могу придумать, это копирование с измененных/удаленных/новых файлов где-то в стороне и последующее копирование их обратно в исходное местоположение после их отмены.

ли мои догадки действительно правильные способы сделать это, есть некоторые предостережения для них, что я не в курсе, или необходимо отменить и Откат быть реализован совершенно по-разному в этом случае?

ответ

1

Если вы просто хотите сохранить файлы в исходной папке, я думаю, будет просто скопировать их в третьей папке (назовем ее tmp), и если пользователь не отменяет операцию, просто переименуйте его в цель, иначе удалите его.

1
  1. Булевы вроде бы в порядке, но нет, если вы когда-нибудь подумали об использовании его многопоточности, особенно потому, что весь класс ist static. В этом случае вы должны использовать токен (только некоторый объект) с синхронизированным логическим свойством, чтобы избежать каких-либо проблем. Afaik нет стандартизованного класса или подхода.
  2. Вам необходимо как-то сохранить копию. Для нескольких и небольших файлов вы можете просто использовать память, но для большей суммы или размера вам придется хранить файлы где-то в файловой системе до синхронизации. Но вам нужно только скопировать файлы, которые вы хотите синхронизировать, чтобы они зависели от процесса. Как и Хане, вы можете переименовать их после/заранее для удобства. Но, в конце концов, всегда бывает так мало, насколько возможно, там, где он все еще работает, и отмена больше не будет вариантом.

Может быть, немного иллюстрации для 2 .: файла А в файл Б

Путь один:

Store B in memory -> copy A to B -> Point of save return -> Delete B in memory 
Rollback memory B to file B 

Путь два:

Store B in C > copy A to B -> Point of save return -> Delete C 
Rollback copy C to B 

Way три (hasnae):

Copy A in C > rename B to D -> rename/move C to B -> Point of save return -> Delete D 
Rollback delete C 
1

Я реализовал что-то очень похожее несколько лет назад, и с тех пор он зарекомендовал себя очень хорошо.

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

  1. У вас все в порядке. Попросите нескольких работников потока удалить ваши предметы (файлы) и на каждой итерации проверьте флаг паузы. Sleep/Wake их при необходимости.

  2. Для нас было лучше использовать расширения файлов, так же как и для версий. Так, например, вы в конечном итоге:

    • destination.exe (старый файл)
    • destination.exe.v2 (новый файл)

      или:

    • destination.exe (новый файл)

    • destination.exe.bak (старый файл)

По завершении просто применяйте окончательные имена (подтвердите синхронизацию/откат).

1

Лучший способ сделать это, чтобы сделать следующее

  1. Определить список неизменных файлов
  2. Определить список удаленных файлов
  3. Определить файлы, которые вы должны скопировать

После того, как вы это сделаете, на том же диске, что и в каталоге, который вы синхронизируете, создайте новый каталог.

Для списка файлов, которые не изменяются, создайте жесткую ссылку для того же файла в новый каталог (с тем же именем и с тем же относительным положением в дереве).

Для удаленных файлов вы ничего не делаете.

Для новых файлов вы просто скопируете их в новую структуру каталогов.

Когда все будет завершено, переименуйте старый каталог во временное. Переименуйте новый каталог в исходное имя каталога. Затем, наконец, удалите старый каталог.

Если вам когда-либо понадобится «откат», просто удалите новый каталог.

Преимущество этого в том, что файлы, которые не изменились, не требуют использования нового дискового пространства и не теряют времени на копирование данных, которые у вас уже есть. Жесткая ссылка в основном является другим указателем на одни и те же данные. Вот почему вы должны делать это на том же диске, это трюк в каталоге. Вы никогда не теряете удаленные файлы (вы просто не связываете их), а затем у вас есть новые копии в новой папке.

В конце концов, только копирование файла, которое выполняется, это файлы, которые были изменены.

 Смежные вопросы

  • Нет связанных вопросов^_^