如何在java中单向同步两个目录结构中的文件?

How to one-way synchronize files in two directory structures in java?

我有两个文件夹,源和目标,里面有文件和可能的子文件夹(假定目录结构相同,子文件夹和文件可以进入任何深度)。我们要同步目标,以便所有文件:

Exists in source, but not in target -> new, copy over to target
Exists in target, but not in source -> deleted, delete from the target
Exists in both, but binary unequal -> changed, copy over from source
Exists in both, and is binary equal -> unchanged, leave be

我遇到的一个问题是检查文件是否存在(listFiles() 的 return 值似乎没有定义 contains()),但更大的障碍是引用其他目录结构。例如,在遍历源文件夹并在那里找到文件时,我如何检查目标文件夹是否包含文件 "foo.txt"?这是我目前所拥有的:

    public void synchronize(File source, File target) {
    //first loop; accounts for every case except deleted
    if (source.isDirectory()) {
        for (File i : source.listFiles()) {
            if (i.isDirectory()) {
                synchronize(i, /**i's equivalent subdirectory in target*/);
            }
            else if (/**i is new*/) {
                /**Copy i over to the appropriate target folder*/
            }
            else if (/**i is different*/) {
                /**copy i over from source to target*/
            }
            else {/**i is identical in both*/
                /**leave i in target alone*/
            }
        }
        for (File i : target.listFiles()) {
            if (/**i exists in the target but not in source*/) {
                /**delete in target*/
            }
        }
    }
}

EDIT(important): 感谢大家的所有回答,但主要问题仍未解决:指的是其他目录,即评论中的内容。 h22 的答案似乎在大概的某个地方,但这还不够,正如下面的评论中所解释的那样。如果有人能用更小的词来解释这一点,我将不胜感激。根据经验,这正是更 java 精明的人可以在五分钟内解决的问题,而我会花两个令人沮丧的星期重新发现美国。

如果你有一个目标目录 File targetDir 和一个源目录中的源文件 File sourceFile 你可以通过写来检查相应的目标文件是否存在:

 File targetFile = new File(targetDir, sourceFile.getName());
 boolean exists  = targetFile.exists();

正如 wero 指出的那样,您可以使用 aFile.exists() 查看给定路径是否存在。您还应该将它与 aFile.isFile() 结合使用以检查路径是否是普通文件(而不是文件夹)。

检查 content-equals 比较棘手。我提出以下建议:

 boolean sameContents(File fa, File fb) throws IOException {
      Path a = a.toPath();
      Path b = b.toPath();
      if (Files.size(a) != Files.size(b)) return false;
      return Arrays.equals(
           Files.readAllBytes(a), Files.readAllBytes(b));
 }

但前提是文件很小;否则你可能 运行 内存不足试图一次比较它们(需要使用 Arrays.equals)。如果那里有大文件,this answer 建议使用 Apache Commons IO 的 FileUtils.contentEquals()

注意上面的代码和contentEquals都只比较文件,不比较文件夹。要比较文件夹,您需要使用递归,对每个同名、相同大小的文件调用 sameContents 或等效方法,如果在源或目标中找不到特定路径名的匹配项,则会出错。

只递归访问源文件夹。剥离文件夹根目录并直接定位目标位置:

String subPath = sourceFile.getAbsolutePath().substring(sourceRoot.length);
File targetFile = new File(targetRoot + File.separator + subPath);

if (targetFile.getParentFile().exists()) {
  targetFile.getParentFile().mkdirs();
}
// copy, etc

否则,如果目标位置缺少所需的分层文件夹结构,可能会深入许多目录,您可能会遇到困难。