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 character
fileOp.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);
}
else
DeleteDir(childName);
}
Directory.Delete(dir, true);
}
}
注意:方法2有一个漏洞,该方法无法成功捕捉到源文件夹下被占用的文件信息!