你們是否是日常都有好多文件須要按期備份?如歌曲、視頻、文檔,代碼文件等等,若是常常增長刪除修改文件,就須要按期備份,最先以前文件都不大的時候我都是手工先所有刪除,而後再所有拷貝,感受比較保險。後來有了很大的電影文件和很瑣碎的代碼文件以後,這樣搞太折磨人,就學網上說的用Xcpoy組裝了一個批處理。學了C#後,感受仍是作一個GUI體驗更好用起來更方便。至於專業的工具,還真沒怎麼試過,有點不放心吧,有好用的卻是能夠試試。如今先本身作一個用着吧。windows
關鍵代碼以下:async
private async void btnBackUp_Click(object sender, EventArgs e) { string sourceDirectory = txtSource.Text; string targetDirectory = txtTarget.Text; if (sourceDirectory.ToLower() == targetDirectory.ToLower()) { Console.WriteLine("源目錄和備份目錄不能是同一目錄!"); MessageBox.Show("源目錄和備份目錄不能是同一目錄!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } DirectoryInfo diSource = new DirectoryInfo(sourceDirectory); // 源目錄 DirectoryInfo diTarget = new DirectoryInfo(targetDirectory); // 備份目錄 if (diTarget.Name != diSource.Name) diTarget = new DirectoryInfo(Path.Combine(diTarget.FullName, diSource.Name)); // 建立同名目錄 if (!diTarget.Exists) diTarget.Create(); // 若是該目錄已存在,則此方法不執行任何操做 btnBackUp.Enabled = false; txtSource.Enabled = false; txtTarget.Enabled = false; lblWork.Text = "備份開始!"; if (await CopyAllAsync(diSource, diTarget)) { lblWork.Text = "備份完成!"; MessageBox.Show("備份完畢!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } else lblWork.Text = "出現錯誤!"; btnBackUp.Enabled = true; txtSource.Enabled = true; txtTarget.Enabled = true; btnBackUp.Focus(); } public async Task<bool> CopyAllAsync(DirectoryInfo source, DirectoryInfo target) { try { foreach (FileInfo fi in source.GetFiles()) // 複製最新文件 { Console.WriteLine(@"準備複製文件 {0}\{1}", target.FullName, fi.Name); // Name不含路徑,僅文件名 FileInfo newfi = new FileInfo(Path.Combine(target.FullName, fi.Name)); if (!newfi.Exists || (newfi.Exists && fi.LastWriteTime > newfi.LastWriteTime)) { Console.WriteLine("正在複製文件 {0}", newfi.FullName); lblWork.Text = string.Format("正在複製文件 {0}", newfi.FullName); if (newfi.Exists && newfi.IsReadOnly) newfi.IsReadOnly = false; // 覆蓋或刪除只讀文件會產生異常:對路徑「XXX」的訪問被拒絕 fi.CopyTo(newfi.FullName, true); // Copy each file into it's new directory } } foreach (FileInfo fi2 in target.GetFiles()) // 刪除源目錄沒有而目標目錄中有的文件 { FileInfo newfi2 = new FileInfo(Path.Combine(source.FullName, fi2.Name)); if (!newfi2.Exists) { Console.WriteLine("正在刪除文件 {0}", fi2.FullName); lblWork.Text = string.Format("正在刪除文件 {0}", fi2.FullName); if (fi2.IsReadOnly) fi2.IsReadOnly = false; fi2.Delete(); // 沒有權限(如系統盤需管理員權限)會產生異常,文件不存在不會產生異常 } } foreach (DirectoryInfo di in source.GetDirectories()) // 複製目錄(其實是建立同名目錄,和源目錄的屬性不一樣步) { Console.WriteLine(" {0} {1}", di.FullName, di.Name); // Name不含路徑,僅本級目錄名 Console.WriteLine(@"準備建立目錄 {0}\{1}", target.FullName, di.Name); DirectoryInfo newdi = new DirectoryInfo(Path.Combine(target.FullName, di.Name)); if (!newdi.Exists) // 若是CopyAllAsync放在if裏的bug: 只要存在同名目錄,則不會進行子目錄和子文件的檢查和更新 { Console.WriteLine("正在建立目錄 {0}", newdi.FullName); lblWork.Text = string.Format("正在複製目錄 {0}", newdi.FullName); DirectoryInfo diTargetSubDir = target.CreateSubdirectory(di.Name); // 建立目錄 Console.WriteLine("完成建立目錄 {0}", diTargetSubDir.FullName); } if (await CopyAllAsync(di, newdi) == false) return false; ; // Copy each subdirectory using recursion } foreach (DirectoryInfo di2 in target.GetDirectories()) // 刪除源目錄沒有而目標目錄中有的目錄(及其子目錄和文件) { DirectoryInfo newdi2 = new DirectoryInfo(Path.Combine(source.FullName, di2.Name)); if (!newdi2.Exists) { Console.WriteLine("正在刪除目錄 {0}", di2.FullName); lblWork.Text = string.Format("正在刪除目錄 {0}", di2.FullName); di2.Delete(true); // 只讀的目錄和文件也能刪除,如不使用參數則異常"目錄不是空的" } } return true; } catch (Exception e) { Console.WriteLine(e.Message); MessageBox.Show(e.Message, "提示", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } }
注意事項:工具
// 文件和目錄的建立日期爲首次全新複製時的建立時間
// 文件複製後修改日期始終保持原先的不變,目錄的修改日期爲首次全新複製時的建立時間(由於本就是新建)
// 單純的覆蓋不會改變修改時間和建立時間
// 文件發生的屬性變化全新複製時能夠保留(沒法經過更新時間判斷文件的屬性變化)測試
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------spa
今天測試,又發現一個bug,真是防不勝防,好在終於找到病根並解決了。線程
問題出在 if (await CopyAllAsync(diSource, diTarget)) 這個地方,備份開始後,lblWork.Text = "備份開始!"; 結果發現標籤的設置並不生效,而後界面很卡,不能拖動窗口。在須要備份更新的文件特別多時感受更明顯。code
原來,設置控件的Enabled屬性是當即生效,但控件的Text屬性並非當即生效,就是UI界面不會當即更新,只是將設置信息加入了windows消息隊列,一般等所在的方法執行完畢後才生效,但若是方法中該語句後面還有同類的設置,就會感受不到它的生效,實際上是生效了,只是先設爲了一個值,而後又當即設爲了另外一個值,由於太快了,人眼看不出來。一樣的緣由,「正在複製文件XXX」也不即時顯示正在複製的文件信息。orm
而後,界面卡頓,是由於拷貝的時候執行緊密運算,可是CopyAllAsync(diSource, diTarget)方法並無在單獨的線程運行,佔用了UI線程,致使界面卡頓,改爲下面這樣,完美解決:視頻
lblWork.Text = "備份開始!";
bool result = await Task.Run(() => CopyAllAsync(diSource, diTarget)); // 這兒是關鍵 if (result) // if (await CopyAllAsync(diSource, diTarget)) 開始後界面會卡
{ lblWork.Text = "備份完成!"; MessageBox.Show("備份完畢!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); } else lblWork.Text = "出現錯誤!";
更新:添加了瀏覽選擇目錄功能,能夠不用手動輸入路徑了。點我下載源碼及可執行文件blog