Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
3.7k views
in Technique[技术] by (71.8m points)

Sync two directories (update target dir from source dir) in C# / .NET

I want to sync two directories, such that after the operation the target dir is an exact copy of the source dir. New source files / sub directories are added to the target, newer source files replace the older target versions, files missing in the source get deleted from the target.

I know I can recursively go through both trees, check for existence and compare last write dates.

My question: Is there something out of the box I can use instead of writing my own code? Something like TargetDir.UpdateFromSourceDir(SourceDir); :-)

I found System.DirectoryServices with the DirectorySynchronization class here: https://docs.microsoft.com/en-us/dotnet/api/system.directoryservices.directorysynchronization?view=dotnet-plat-ext-5.0. From its name it sounds like it might do what I want, but from its methods I doubt that.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

There are as many ways to do this as lines of code in the example I will show.

  • The easiest way is to use a sync library
  • Failing that, is to delete and copy the directory.
  • If you don't want that, the safest way (albeit prone to more data races) is to hash all the files, make a list of files to remove, add, update.
  • The unsafest way is to do the above but instead use file date

Personally id just delete and copy and move on to something else, however you could try this.

Forewarning : Use a sync library if you can find one. This is purely for academic purposes and doesn't equate to the worlds best solution

Given

public static (HashSet<string> dirs, HashSet<(string file,long tick)> files) Get(string dir)
{

   string CleanPath(string path) => path.Substring(dir.Length).TrimStart('/', '\');

   // replace this with a hashing method if you need
   long GetUnique(string path) => new FileInfo(path).LastWriteTime.Ticks;

   var directories = Directory.GetDirectories(dir, "*.*", SearchOption.AllDirectories);

   var dirHash = directories.Select(CleanPath).ToHashSet();

   // this could be paralleled if need be (if using a hash) 
   var fileHash = directories.SelectMany(Directory.EnumerateFiles)
      .Select(file => (name: CleanPath(file), ticks : GetUnique(file)))
      .ToHashSet();

   return (dirHash, fileHash);    
}

Usage

var result1 = Get(dir1);
var result2 = Get(dir2);

var dirsToRemove = result2.dirs.Where(x => !result1.dirs.Contains(x));
var filesToRemove = result2.files.Where(x => !result1.files.Contains(x));
var filesToAdd = result1.files.Where(x => !result2.files.Contains(x));

foreach (var fileToRemove in filesToRemove)
   Console.WriteLine("Deleting : " + Path.Combine(dir2, fileToRemove.file));

foreach (var dirToRemove in dirsToRemove)
   Console.WriteLine("Deleting : " + Path.Combine(dir2, dirToRemove));

foreach (var fileToAdd in filesToAdd)
   Console.WriteLine("Adding : " + Path.Combine(dir2,fileToAdd.file));

Note : There are many ways for this to fail, the assumption is you have permissions to do this and files aren't locked. This also should have a significant amount of error checking, may need to be customized, and you should use an extreme amount of due diligence before you use it (be warned)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...