フォルダをコピーする際に、コピー元のファイルがコピー先に存在しないか、コピー先のファイルより新しい場合に限りコピーし、さらに2つのフォルダのファイル構成が同じになるようにミラーリング(同期)する方法を紹介します。
まずフォルダのコピーについては、「フォルダをコピーする」をご覧ください。
ここではこのコードに手を加えることにします。
まず、File.ExistsメソッドとFile.GetLastWriteTimeメソッドを使い、コピー元のファイルがコピー先に存在しないか、またはコピー先のファイルより新しいか調べ、ファイルをコピーするようにします。
さらにミラーリングをするために、コピー先にあってコピー元にないファイルを探して削除するためのメソッド(ここでは"DeleteNotExistFiles")を作成します。
このようにして作成された新たなCopyDirectoryメソッドは次のようなものです。
'Imports System.IO 'がソースファイルの一番上に書かれているものとする ''' <summary> ''' ディレクトリをコピーする ''' </summary> ''' <param name="sourceDirName">コピーするディレクトリ</param> ''' <param name="destDirName">コピー先のディレクトリ</param> ''' <param name="newerOnly">新しいファイルのみコピーする</param> ''' <param name="sync">sourceDirNameにないファイルを削除する</param> Public Shared Sub CopyDirectory(ByVal sourceDirName As String, _ ByVal destDirName As String, _ ByVal newerOnly As Boolean, _ ByVal sync As Boolean) 'コピー先のディレクトリがないときは作る If Not Directory.Exists(destDirName) Then Directory.CreateDirectory(destDirName) '属性もコピー File.SetAttributes(destDirName, File.GetAttributes(sourceDirName)) End If 'コピー先のディレクトリ名の末尾に"\"をつける If destDirName(destDirName.Length - 1) <> Path.DirectorySeparatorChar Then destDirName = destDirName + Path.DirectorySeparatorChar End If 'コピー元のディレクトリにあるファイルをコピー Dim files As String() = Directory.GetFiles(sourceDirName) Dim f As String For Each f In files Dim destFileName As String = destDirName + Path.GetFileName(f) 'コピー先にファイルが存在し、 'コピー元より更新日時が古い時はコピーする If Not newerOnly OrElse _ Not File.Exists(destFileName) OrElse _ File.GetLastWriteTime(destFileName) < File.GetLastWriteTime(f) Then File.Copy(f, destFileName, True) End If Next 'コピー先にあってコピー元にないファイルを削除 If sync Then DeleteNotExistFiles(sourceDirName, destDirName) End If 'コピー元のディレクトリにあるディレクトリについて、再帰的に呼び出す Dim dirs As String() = Directory.GetDirectories(sourceDirName) Dim dir As String For Each dir In dirs CopyDirectory(dir, destDirName + Path.GetFileName(dir), newerOnly, sync) Next End Sub ''' <summary> ''' destDirNameにありsourceDirNameにないファイルを削除する ''' </summary> ''' <param name="sourceDirName">比較先のフォルダ</param> ''' <param name="destDirName">比較もとのフォルダ</param> Private Shared Sub DeleteNotExistFiles(ByVal sourceDirName As String, _ ByVal destDirName As String) 'sourceDirNameの末尾に"\"をつける If sourceDirName(sourceDirName.Length - 1) <> Path.DirectorySeparatorChar Then sourceDirName = sourceDirName + Path.DirectorySeparatorChar End If 'destDirNameにありsourceDirNameにないファイルを削除する Dim files As String() = Directory.GetFiles(destDirName) Dim f As String For Each f In files If Not File.Exists(sourceDirName + Path.GetFileName(f)) Then File.Delete(f) End If Next 'destDirNameにありsourceDirNameにないフォルダを削除する Dim folders As String() = Directory.GetDirectories(destDirName) Dim folder As String For Each folder In folders If Not Directory.Exists(sourceDirName + Path.GetFileName(folder)) Then Directory.Delete(folder, True) End If Next End Sub
//using System.IO; //がソースファイルの一番上に書かれているものとする /// <summary> /// ディレクトリをコピーする /// </summary> /// <param name="sourceDirName">コピーするディレクトリ</param> /// <param name="destDirName">コピー先のディレクトリ</param> /// <param name="newerOnly">新しいファイルのみコピーする</param> /// <param name="sync">sourceDirNameにないファイルを削除する</param> public static void CopyDirectory( string sourceDirName, string destDirName, bool newerOnly, bool sync) { //コピー先のディレクトリがないときは作る if (!Directory.Exists(destDirName)) { Directory.CreateDirectory(destDirName); //属性もコピー File.SetAttributes(destDirName, File.GetAttributes(sourceDirName)); } //コピー先のディレクトリ名の末尾に"\"をつける if (destDirName[destDirName.Length - 1] != Path.DirectorySeparatorChar) { destDirName = destDirName + Path.DirectorySeparatorChar; } //コピー元のディレクトリにあるファイルをコピー string[] files = Directory.GetFiles(sourceDirName); foreach (string f in files) { string destFileName = destDirName + Path.GetFileName(f); //コピー先にファイルが存在し、 //コピー元より更新日時が古い時はコピーする if (!newerOnly || !File.Exists(destFileName) || File.GetLastWriteTime(destFileName) < File.GetLastWriteTime(f)) { File.Copy(f, destFileName, true); } } //コピー先にあってコピー元にないファイルを削除 if (sync) { DeleteNotExistFiles(sourceDirName, destDirName); } //コピー元のディレクトリにあるディレクトリについて、再帰的に呼び出す string[] dirs = Directory.GetDirectories(sourceDirName); foreach (string dir in dirs) { CopyDirectory(dir, destDirName + Path.GetFileName(dir), newerOnly, sync); } } /// <summary> /// destDirNameにありsourceDirNameにないファイルを削除する /// </summary> /// <param name="sourceDirName">比較先のフォルダ</param> /// <param name="destDirName">比較もとのフォルダ</param> private static void DeleteNotExistFiles( string sourceDirName, string destDirName) { //sourceDirNameの末尾に"\"をつける if (sourceDirName[sourceDirName.Length - 1] != Path.DirectorySeparatorChar) { sourceDirName = sourceDirName + Path.DirectorySeparatorChar; } //destDirNameにありsourceDirNameにないファイルを削除する string[] files = Directory.GetFiles(destDirName); foreach (string f in files) { if (!File.Exists(sourceDirName + Path.GetFileName(f))) { File.Delete(f); } } //destDirNameにありsourceDirNameにないフォルダを削除する string[] folders = Directory.GetDirectories(destDirName); foreach (string folder in folders) { if (!Directory.Exists(sourceDirName + Path.GetFileName(folder))) { Directory.Delete(folder, true); } } }
次に使用法を示します。フォルダ"C:\test1"内の更新されたファイルをフォルダ"C:\test2"にコピーし、ミラーリングを行うには、次のようにします。
CopyDirectory("C:\test1", "C:\test2", True, True)
CopyDirectory("C:\\test1", "C:\\test2", true, true);
(この記事は、「.NETプログラミング研究 第48号」で紹介したものを基にしています。)