C# 备份目标文件夹
方法1:通过 递归 或者 迭代 结合 C# 方法
参数说明:
- sourceFolder:源文件夹路径
- destinationFolder:目标路径
- excludeNames:源文件夹中不需备份的文件或文件夹路径哈希表
- errorLog:输出错误log
递归实现:
private bool CopyAllFolder(string sourceFolder, string destinationFolder, HashSet<string> excludeNames, out string errorLog){errorLog = string.Empty;try{if (!Directory.Exists(destinationFolder)){Directory.CreateDirectory(destinationFolder);}string[] directories = Directory.GetDirectories(sourceFolder);string[] files = Directory.GetFiles(sourceFolder);foreach (string file in files){if (excludeNames.Count != 0 && excludeNames.Contains(file)){continue;}try{if (!BRTools.IsFileReady(file) || !BRTools.IsNotFileInUse(file, out errorLog)) // 检测文件是否被占用{return false;}string destinationFile = Path.Combine(destinationFolder, Path.GetFileName(file));File.Copy(file, destinationFile, true);}catch (Exception ex){errorLog += $"Error copying file '{file}': {ex.Message}\n";return false;}}foreach (string directory in directories){if (excludeNames.Count != 0 && excludeNames.Contains(directory)){continue;}string destinationSubFolder = Path.Combine(destinationFolder, Path.GetFileName(directory));if (!CopyAllFolder(directory, destinationSubFolder, excludeNames, out string subfolderErrorLog)){errorLog += subfolderErrorLog;return false;}}return true;}catch (Exception ex){errorLog = $"Error during folder copy: Message = '{ex.Message}', StackTrace = '{ex.StackTrace}'\n";return false;}}
迭代实现:
private bool CopyAllFolder(string sourceFolder, string destinationFolder, HashSet<string> excludeNames, out string errorLog){errorLog = string.Empty;try{if (!Directory.Exists(destinationFolder)){Directory.CreateDirectory(destinationFolder);}Stack<string> directoryStack = new Stack<string>();directoryStack.Push(sourceFolder);while (directoryStack.Count > 0){string currentDirectory = directoryStack.Pop();string[] directories = Directory.GetDirectories(currentDirectory);string[] files = Directory.GetFiles(currentDirectory);foreach (string file in files){if (excludeNames.Count != 0 && excludeNames.Contains(file)){continue;}try{if (!BRTools.IsFileReady(file) || !BRTools.IsNotFileInUse(file, out errorLog)){return false;}string destinationFile = Path.Combine(destinationFolder, Path.GetFileName(file));File.Copy(file, destinationFile, true);}catch (Exception ex){errorLog += $"Error copying file '{file}': {ex.Message}\n";return false;}}foreach (string directory in directories){if (excludeNames.Count != 0 && excludeNames.Contains(directory)){continue;}string destinationSubFolder = Path.Combine(destinationFolder, Path.GetFileName(directory));if (!CopyAllFolder(directory, destinationSubFolder, excludeNames, out string subfolderErrorLog)){errorLog += subfolderErrorLog;return false;}directoryStack.Push(directory);}}return true;}catch (Exception ex){errorLog = $"Error during folder copy: Message = '{ex.Message}', StackTrace = '{ex.StackTrace}'\n";return false;}}
方法2:利用 Windows API
[DllImport("shell32.dll", CharSet = CharSet.Auto)]public static extern int SHFileOperation(ref SHFILEOPSTRUCT lpFileOp);[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]public struct SHFILEOPSTRUCT{public IntPtr hwnd;public int wFunc;public string pFrom;public string pTo;public short fFlags;public bool fAnyOperationsAborted;public IntPtr hNameMappings;}const int FO_COPY = 0x0002;const int FOF_NOCONFIRMATION = 0x0010;const int FOF_SILENT = 0x0004;const int FOF_NO_UI = FOF_NOCONFIRMATION | FOF_SILENT;private bool CopyDirectory(string sourceDir, string destDir, out string errorLog){errorLog = string.Empty;try{SHFILEOPSTRUCT fileOp = new SHFILEOPSTRUCT();fileOp.wFunc = FO_COPY;fileOp.pFrom = sourceDir + '\0' + '\0'; // Must end with double null characterfileOp.pTo = destDir + '\0' + '\0'; // Must end with double null character//fileOp.fFlags = FOF_NO_UI;fileOp.fFlags = FOF_NO_UI | FOF_NOCONFIRMATION; // 忽略UI和确认对话框int result = SHFileOperation(ref fileOp);// 检查返回值if (result != 0){errorLog = $"SHFileOperation failed with error code: {result}";return false;}return true;}catch (Exception ex){errorLog = $"Failed to copy the entire folder '{sourceDir}': Message = '{ex.Message}', StackTrace = '{ex.StackTrace}'\n";return false;}}private bool CopyFolder(string sourceFolder, string destinationFolder, HashSet<string> excludeNames, out string errorLog){errorLog = string.Empty;try{if (!CopyDirectory(sourceFolder, destinationFolder, out errorLog)){this.logger.Warning($"errorLog: {errorLog}");return false;}if (excludeNames.Count != 0){foreach (var item in excludeNames){var targetPath = Path.Combine(destinationFolder, GetSonFolderPath(sourceFolder, item)); // 获取已备份路径下需排除的文件夹或文件路径if (Directory.Exists(item)){ DeleteDir(targetPath);}if(File.Exists(item)){DeleteDir(targetPath);}}}return true;}catch(Exception ex){errorLog = $"Error during folder copy, and exception is: Message = '{ex.Message}', StackTrace = '{ex.StackTrace}'\n";return false;}}private string GetSonFolderPath(string folderPath, string targetPath){string result = string.Empty;try{folderPath = folderPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;if (!isFilePath(targetPath)){targetPath = targetPath.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;}else{targetPath = Path.GetDirectoryName(targetPath).TrimEnd(Path.DirectorySeparatorChar);}if (targetPath.StartsWith(folderPath, StringComparison.OrdinalIgnoreCase)){result = targetPath.Substring(folderPath.Length);}}catch (Exception){result = string.Empty;}return result;}private bool isFilePath(string targetPath){if (Path.HasExtension(targetPath) && File.Exists(targetPath))return true;return false;}private void DeleteFile(string file){if (File.Exists(file)){FileInfo fi = new FileInfo(file);if (fi.IsReadOnly){fi.IsReadOnly = false;}File.Delete(file);}}private void DeleteDir(string dir){if (Directory.Exists(dir)){foreach (string childName in Directory.GetFileSystemEntries(dir)){if (File.Exists(childName)){FileInfo fi = new FileInfo(childName);if (fi.IsReadOnly){fi.IsReadOnly = false;}File.Delete(childName);}elseDeleteDir(childName);}Directory.Delete(dir, true);}}
注意:方法2有一个漏洞,该方法无法成功捕捉到源文件夹下被占用的文件信息!