在c#中复制一个目录的全部内容

我想用c#将一个目录的全部内容从一个位置复制到另一个位置。

如果没有大量的递归,使用System.IO类似乎没有办法做到这一点。

在VB中有一个方法,如果我们添加一个对Microsoft.VisualBasic的引用,我们可以使用:

new Microsoft.VisualBasic.Devices.Computer().
FileSystem.CopyDirectory( sourceFolder, outputFolder );

这似乎是一个相当丑陋的黑客。有没有更好的办法?

455191 次浏览

或者,如果你想走一条艰难的路,为你的微软项目添加一个引用。然后使用下面的代码:

Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(fromDirectory, toDirectory);

然而,使用一个递归函数是一个更好的方法,因为它不需要加载VB dll。

抱歉之前的代码,它仍然有bug:((成为最快枪问题的猎物)。这里是测试和工作。键是SearchOption。AllDirectories,这消除了显式递归的需要。

string path = "C:\\a";
string[] dirs = Directory.GetDirectories(path, "*.*", SearchOption.AllDirectories);
string newpath = "C:\\x";
try
{
Directory.CreateDirectory(newpath);
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
}
for (int j = 0; j < dirs.Length; j++)
{
try
{
Directory.CreateDirectory(dirs[j].Replace(path, newpath));
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
}
}


string[] files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
for (int j = 0; j < files.Length; j++)
{
try
{
File.Copy(files[j], files[j].Replace(path, newpath));
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
}
}

下面是我用于IO任务的一个实用程序类。

using System;
using System.Runtime.InteropServices;


namespace MyNameSpace
{
public class ShellFileOperation
{
private static String StringArrayToMultiString(String[] stringArray)
{
String multiString = "";


if (stringArray == null)
return "";


for (int i=0 ; i<stringArray.Length ; i++)
multiString += stringArray[i] + '\0';


multiString += '\0';


return multiString;
}


public static bool Copy(string source, string dest)
{
return Copy(new String[] { source }, new String[] { dest });
}


public static bool Copy(String[] source, String[] dest)
{
Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();


FileOpStruct.hwnd = IntPtr.Zero;
FileOpStruct.wFunc = (uint)Win32.FO_COPY;


String multiSource = StringArrayToMultiString(source);
String multiDest = StringArrayToMultiString(dest);
FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
FileOpStruct.pTo = Marshal.StringToHGlobalUni(multiDest);


FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION;
FileOpStruct.lpszProgressTitle = "";
FileOpStruct.fAnyOperationsAborted = 0;
FileOpStruct.hNameMappings = IntPtr.Zero;


int retval = Win32.SHFileOperation(ref FileOpStruct);


if(retval != 0) return false;
return true;
}


public static bool Move(string source, string dest)
{
return Move(new String[] { source }, new String[] { dest });
}


public static bool Delete(string file)
{
Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();


FileOpStruct.hwnd = IntPtr.Zero;
FileOpStruct.wFunc = (uint)Win32.FO_DELETE;


String multiSource = StringArrayToMultiString(new string[] { file });
FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
FileOpStruct.pTo =  IntPtr.Zero;


FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_SILENT | (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION | (ushort)Win32.ShellFileOperationFlags.FOF_NOERRORUI | (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMMKDIR;
FileOpStruct.lpszProgressTitle = "";
FileOpStruct.fAnyOperationsAborted = 0;
FileOpStruct.hNameMappings = IntPtr.Zero;


int retval = Win32.SHFileOperation(ref FileOpStruct);


if(retval != 0) return false;
return true;
}


public static bool Move(String[] source, String[] dest)
{
Win32.SHFILEOPSTRUCT FileOpStruct = new Win32.SHFILEOPSTRUCT();


FileOpStruct.hwnd = IntPtr.Zero;
FileOpStruct.wFunc = (uint)Win32.FO_MOVE;


String multiSource = StringArrayToMultiString(source);
String multiDest = StringArrayToMultiString(dest);
FileOpStruct.pFrom = Marshal.StringToHGlobalUni(multiSource);
FileOpStruct.pTo = Marshal.StringToHGlobalUni(multiDest);


FileOpStruct.fFlags = (ushort)Win32.ShellFileOperationFlags.FOF_NOCONFIRMATION;
FileOpStruct.lpszProgressTitle = "";
FileOpStruct.fAnyOperationsAborted = 0;
FileOpStruct.hNameMappings = IntPtr.Zero;


int retval = Win32.SHFileOperation(ref FileOpStruct);


if(retval != 0) return false;
return true;
}
}
}

嗯,我想我误解了这个问题,但我要冒这个险。下面这个简单的方法有什么问题?

public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) {
foreach (DirectoryInfo dir in source.GetDirectories())
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
foreach (FileInfo file in source.GetFiles())
file.CopyTo(Path.Combine(target.FullName, file.Name));
}

编辑由于这篇文章为一个同样简单的问题提供了如此简单的答案,获得了令人印象深刻的反对票,让我补充一个解释。# EYZ1 # EYZ2。

首先,这段代码并不是用来替换的到问题中的代码。这只是为了说明。

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectory执行一些额外的正确性测试(例如,源和目标是否为有效目录,源是否是目标的父目录等等),这些都是这个答案中所没有的。该代码可能也更加优化了。

也就是说,代码工作得很好。它已经在一个成熟的软件中使用了很多年了。除了所有IO处理固有的变化无常(例如,如果用户在代码写入USB驱动器时手动拔出USB驱动器会发生什么?),没有已知的问题。

特别地,我想指出这里使用递归绝对不是问题。无论是在理论上(概念上,这是最优雅的解决方案)还是在实践中:这段代码不会溢出堆栈。这个堆栈足够大,甚至可以处理嵌套很深的文件层次结构。早在堆栈空间成为问题之前,文件夹路径长度限制就开始生效了。

注意,恶意用户可以通过使用每个字母的深度嵌套目录来打破这个假设。我还没试过。但是为了说明这一点:为了使这段代码在典型的计算机上溢出,目录必须嵌套几次。这是不现实的情况。

试试这个:

Process proc = new Process();
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "xcopy.exe");
proc.StartInfo.Arguments = @"C:\source C:\destination /E /I";
proc.Start();

你的xcopy参数可能会有所不同,但你知道的。

MSDN复制:

using System;
using System.IO;


class CopyDir
{
public static void Copy(string sourceDirectory, string targetDirectory)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);


CopyAll(diSource, diTarget);
}


public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
Directory.CreateDirectory(target.FullName);


// Copy each file into the new directory.
foreach (FileInfo fi in source.GetFiles())
{
Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
}


// Copy each subdirectory using recursion.
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir =
target.CreateSubdirectory(diSourceSubDir.Name);
CopyAll(diSourceSubDir, nextTargetSubDir);
}
}


public static void Main()
{
string sourceDirectory = @"c:\sourceDirectory";
string targetDirectory = @"c:\targetDirectory";


Copy(sourceDirectory, targetDirectory);
}


// Output will vary based on the contents of the source directory.
}

递归复制文件夹而不递归,以避免堆栈溢出。

public static void CopyDirectory(string source, string target)
{
var stack = new Stack<Folders>();
stack.Push(new Folders(source, target));


while (stack.Count > 0)
{
var folders = stack.Pop();
Directory.CreateDirectory(folders.Target);
foreach (var file in Directory.GetFiles(folders.Source, "*.*"))
{
File.Copy(file, Path.Combine(folders.Target, Path.GetFileName(file)));
}


foreach (var folder in Directory.GetDirectories(folders.Source))
{
stack.Push(new Folders(folder, Path.Combine(folders.Target, Path.GetFileName(folder))));
}
}
}


public class Folders
{
public string Source { get; private set; }
public string Target { get; private set; }


public Folders(string source, string target)
{
Source = source;
Target = target;
}
}

容易得多

private static void CopyFilesRecursively(string sourcePath, string targetPath)
{
//Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
}


//Copy all the files & Replaces any files with the same name
foreach (string newPath in Directory.GetFiles(sourcePath, "*.*",SearchOption.AllDirectories))
{
File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true);
}
}

这个网站总是帮助我很多,现在轮到我用我所知道的帮助其他人了。

我希望我下面的代码对某些人有用。

string source_dir = @"E:\";
string destination_dir = @"C:\";


// substring is to remove destination_dir absolute path (E:\).


// Create subdirectory structure in destination
foreach (string dir in System.IO.Directory.GetDirectories(source_dir, "*", System.IO.SearchOption.AllDirectories))
{
System.IO.Directory.CreateDirectory(System.IO.Path.Combine(destination_dir, dir.Substring(source_dir.Length + 1)));
// Example:
//     > C:\sources (and not C:\E:\sources)
}


foreach (string file_name in System.IO.Directory.GetFiles(source_dir, "*", System.IO.SearchOption.AllDirectories))
{
System.IO.File.Copy(file_name, System.IO.Path.Combine(destination_dir, file_name.Substring(source_dir.Length + 1)));
}

对于d4nt的答案有一个小的改进,因为如果你在服务器和开发机器上工作,你可能想检查错误,而不必更改xcopy路径:

public void CopyFolder(string source, string destination)
{
string xcopyPath = Environment.GetEnvironmentVariable("WINDIR") + @"\System32\xcopy.exe";
ProcessStartInfo info = new ProcessStartInfo(xcopyPath);
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
info.Arguments = string.Format("\"{0}\" \"{1}\" /E /I", source, destination);


Process process = Process.Start(info);
process.WaitForExit();
string result = process.StandardOutput.ReadToEnd();


if (process.ExitCode != 0)
{
// Or your own custom exception, or just return false if you prefer.
throw new InvalidOperationException(string.Format("Failed to copy {0} to {1}: {2}", source, destination, result));
}
}

这是我的代码,希望对大家有所帮助

    private void KCOPY(string source, string destination)
{
if (IsFile(source))
{
string target = Path.Combine(destination, Path.GetFileName(source));
File.Copy(source, target, true);
}
else
{
string fileName = Path.GetFileName(source);
string target = System.IO.Path.Combine(destination, fileName);
if (!System.IO.Directory.Exists(target))
{
System.IO.Directory.CreateDirectory(target);
}


List<string> files = GetAllFileAndFolder(source);


foreach (string file in files)
{
KCOPY(file, target);
}
}
}


private List<string> GetAllFileAndFolder(string path)
{
List<string> allFile = new List<string>();
foreach (string dir in Directory.GetDirectories(path))
{
allFile.Add(dir);
}
foreach (string file in Directory.GetFiles(path))
{
allFile.Add(file);
}


return allFile;
}
private bool IsFile(string path)
{
if ((File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory)
{
return false;
}
return true;
}

下面是DirectoryInfo的一个扩展方法FileInfo。CopyTo(注意overwrite参数):

public static DirectoryInfo CopyTo(this DirectoryInfo sourceDir, string destinationPath, bool overwrite = false)
{
var sourcePath = sourceDir.FullName;


var destination = new DirectoryInfo(destinationPath);


destination.Create();


foreach (var sourceSubDirPath in Directory.EnumerateDirectories(sourcePath, "*", SearchOption.AllDirectories))
Directory.CreateDirectory(sourceSubDirPath.Replace(sourcePath, destinationPath));


foreach (var file in Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories))
File.Copy(file, file.Replace(sourcePath, destinationPath), overwrite);


return destination;
}

如果您喜欢Konrad的流行答案,但您希望source本身是target下的一个文件夹,而不是将它的子文件夹放在target文件夹下,下面是代码。它返回新创建的DirectoryInfo,这很方便:

public static DirectoryInfo CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
var newDirectoryInfo = target.CreateSubdirectory(source.Name);
foreach (var fileInfo in source.GetFiles())
fileInfo.CopyTo(Path.Combine(newDirectoryInfo.FullName, fileInfo.Name));


foreach (var childDirectoryInfo in source.GetDirectories())
CopyFilesRecursively(childDirectoryInfo, newDirectoryInfo);


return newDirectoryInfo;
}

tboswell的替换证明版本(这是弹性的重复模式在文件路径)

public static void copyAll(string SourcePath , string DestinationPath )
{
//Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(SourcePath, "*", SearchOption.AllDirectories))
Directory.CreateDirectory(Path.Combine(DestinationPath ,dirPath.Remove(0, SourcePath.Length ))  );


//Copy all the files & Replaces any files with the same name
foreach (string newPath in Directory.GetFiles(SourcePath, "*.*",  SearchOption.AllDirectories))
File.Copy(newPath, Path.Combine(DestinationPath , newPath.Remove(0, SourcePath.Length)) , true);
}

你可以使用,取自微软网站。

static void Main()
{
// Copy from the current directory, include subdirectories.
DirectoryCopy(".", @".\temp", true);
}


private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);


if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}


DirectoryInfo[] dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}


// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, false);
}


// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}

使用这个类。

public static class Extensions
{
public static void CopyTo(this DirectoryInfo source, DirectoryInfo target, bool overwiteFiles = true)
{
if (!source.Exists) return;
if (!target.Exists) target.Create();


Parallel.ForEach(source.GetDirectories(), (sourceChildDirectory) =>
CopyTo(sourceChildDirectory, new DirectoryInfo(Path.Combine(target.FullName, sourceChildDirectory.Name))));


foreach (var sourceFile in source.GetFiles())
sourceFile.CopyTo(Path.Combine(target.FullName, sourceFile.Name), overwiteFiles);
}
public static void CopyTo(this DirectoryInfo source, string target, bool overwiteFiles = true)
{
CopyTo(source, new DirectoryInfo(target), overwiteFiles);
}
}

比任何代码都好(使用递归扩展DirectoryInfo的方法)

public static bool CopyTo(this DirectoryInfo source, string destination)
{
try
{
foreach (string dirPath in Directory.GetDirectories(source.FullName))
{
var newDirPath = dirPath.Replace(source.FullName, destination);
Directory.CreateDirectory(newDirPath);
new DirectoryInfo(dirPath).CopyTo(newDirPath);
}
//Copy all the files & Replaces any files with the same name
foreach (string filePath in Directory.GetFiles(source.FullName))
{
File.Copy(filePath, filePath.Replace(source.FullName,destination), true);
}
return true;
}
catch (IOException exp)
{
return false;
}
}

一个只有一个循环复制所有文件夹和文件的变体:

foreach (var f in Directory.GetFileSystemEntries(path, "*", SearchOption.AllDirectories))
{
var output = Regex.Replace(f, @"^" + path, newPath);
if (File.Exists(f)) File.Copy(f, output, true);
else Directory.CreateDirectory(output);
}

它可能没有性能意识,但我用它来处理30MB的文件夹,它工作得完美无缺。另外,我不喜欢这么简单的任务所需要的大量代码和递归。

var src = "c:\src";
var dest = "c:\dest";
var cmp = CompressionLevel.NoCompression;
var zip = source_folder + ".zip";


ZipFile.CreateFromDirectory(src, zip, cmp, includeBaseDirectory: false);
ZipFile.ExtractToDirectory(zip, dest_folder);


File.Delete(zip);

注意:ZipFile可以在。net 4.5+的System.IO.Compression命名空间中使用

我的解决方案基本上是对@Termininja的答案的修改,但我增强了一点,它似乎比公认的答案快5倍以上。

public static void CopyEntireDirectory(string path, string newPath)
{
Parallel.ForEach(Directory.GetFileSystemEntries(path, "*", SearchOption.AllDirectories)
,(fileName) =>
{
string output = Regex.Replace(fileName, "^" + Regex.Escape(path), newPath);
if (File.Exists(fileName))
{
Directory.CreateDirectory(Path.GetDirectoryName(output));
File.Copy(fileName, output, true);
}
else
Directory.CreateDirectory(output);
});
}

编辑:将@Ahmed Sabry修改为完全并行foreach确实会产生更好的结果,但是代码使用递归函数,在某些情况下并不理想。

public static void CopyEntireDirectory(DirectoryInfo source, DirectoryInfo target, bool overwiteFiles = true)
{
if (!source.Exists) return;
if (!target.Exists) target.Create();


Parallel.ForEach(source.GetDirectories(), (sourceChildDirectory) =>
CopyEntireDirectory(sourceChildDirectory, new DirectoryInfo(Path.Combine(target.FullName, sourceChildDirectory.Name))));


Parallel.ForEach(source.GetFiles(), sourceFile =>
sourceFile.CopyTo(Path.Combine(target.FullName, sourceFile.Name), overwiteFiles));
}

复制并替换文件夹中的所有文件

        public static void CopyAndReplaceAll(string SourcePath, string DestinationPath, string backupPath)
{
foreach (string dirPath in Directory.GetDirectories(SourcePath, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory($"{DestinationPath}{dirPath.Remove(0, SourcePath.Length)}");
Directory.CreateDirectory($"{backupPath}{dirPath.Remove(0, SourcePath.Length)}");
}
foreach (string newPath in Directory.GetFiles(SourcePath, "*.*", SearchOption.AllDirectories))
{
if (!File.Exists($"{ DestinationPath}{newPath.Remove(0, SourcePath.Length)}"))
File.Copy(newPath, $"{ DestinationPath}{newPath.Remove(0, SourcePath.Length)}");
else
File.Replace(newPath
, $"{ DestinationPath}{newPath.Remove(0, SourcePath.Length)}"
, $"{ backupPath}{newPath.Remove(0, SourcePath.Length)}", false);
}
}

下面的代码是microsoft suggestion how-to-copy-directories 亲爱的@iato分享了它 但是它是只是递归地复制源文件夹的子目录和文件不复制自己的源文件夹(像右击->复制)

有一个棘手的方法下面的答案:

private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs = true)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);


if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}


DirectoryInfo[] dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}


// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, false);
}


// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}

如果你想递归复制文件夹和子文件夹文件夹中的内容,你可以简单地像这样使用它:

string source = @"J:\source\";
string dest= @"J:\destination\";
DirectoryCopy(source, dest);

但如果你想复制源目录本身(类似于你右键单击源文件夹,点击复制,然后在目标文件夹点击粘贴),你应该像这样使用:

 string source = @"J:\source\";
string dest= @"J:\destination\";
DirectoryCopy(source, Path.Combine(dest, new DirectoryInfo(source).Name));

下面的代码复制所有文件从源到目标给定的模式在相同的文件夹结构:

public static void Copy()
{
string sourceDir = @"C:\test\source\";
string destination = @"C:\test\destination\";


string[] textFiles = Directory.GetFiles(sourceDir, "*.txt", SearchOption.AllDirectories);


foreach (string textFile in textFiles)
{
string fileName = textFile.Substring(sourceDir.Length);
string directoryPath = Path.Combine(destination, Path.GetDirectoryName(fileName));
if (!Directory.Exists(directoryPath))
Directory.CreateDirectory(directoryPath);


File.Copy(textFile, Path.Combine(directoryPath, Path.GetFileName(textFile)), true);
}
}

enter image description here

这里有一个简洁而有效的解决方案:

namespace System.IO {
public static class ExtensionMethods {


public static void CopyTo(this DirectoryInfo srcPath, string destPath) {
Directory.CreateDirectory(destPath);
Parallel.ForEach(srcPath.GetDirectories("*", SearchOption.AllDirectories),
srcInfo => Directory.CreateDirectory($"{destPath}{srcInfo.FullName[srcPath.FullName.Length..]}"));
Parallel.ForEach(srcPath.GetFiles("*", SearchOption.AllDirectories),
srcInfo => File.Copy(srcInfo.FullName, $"{destPath}{srcInfo.FullName[srcPath.FullName.Length..]}", true));
});
}


}
}

使用方法:

new DirectoryInfo(sourcePath).CopyTo(destinationPath);

只是想加上我的版本。它可以处理目录和文件,如果目标文件存在,则可以覆盖或跳过。

public static void Copy(
string source,
string destination,
string pattern = "*",
bool includeSubFolders = true,
bool overwrite = true,
bool overwriteOnlyIfSourceIsNewer = false)
{
if (File.Exists(source))
{
// Source is a file, copy and leave
CopyFile(source, destination);
return;
}


if (!Directory.Exists(source))
{
throw new DirectoryNotFoundException($"Source directory does not exists: `{source}`");
}


var files = Directory.GetFiles(
source,
pattern,
includeSubFolders ?
SearchOption.AllDirectories :
SearchOption.TopDirectoryOnly);


foreach (var file in files)
{
var newFile = file.Replace(source, destination);
CopyFile(file, newFile, overwrite, overwriteOnlyIfSourceIsNewer);
}
}


private static void CopyFile(
string source,
string destination,
bool overwrite = true,
bool overwriteIfSourceIsNewer = false)
{
if (!overwrite && File.Exists(destination))
{
return;
}


if (overwriteIfSourceIsNewer && File.Exists(destination))
{
var sourceLastModified = File.GetLastWriteTimeUtc(source);
var destinationLastModified = File.GetLastWriteTimeUtc(destination);
if (sourceLastModified <= destinationLastModified)
{
return;
}


CreateDirectory(destination);
File.Copy(source, destination, overwrite);
return;
}


CreateDirectory(destination);
File.Copy(source, destination, overwrite);
}


private static void CreateDirectory(string filePath)
{
var targetDirectory = Path.GetDirectoryName(filePath);
if (targetDirectory != null && !Directory.Exists(targetDirectory))
{
Directory.CreateDirectory(targetDirectory);
}
}

这段代码的属性:

  • 没有并行任务,性能较差,但其思想是逐文件处理,因此可以记录或停止。
  • 可以跳过隐藏文件
  • 可以跳过修改日期吗
  • 可以打破或不(您选择)对文件复制错误
  • SMB使用64K的Buffer和FileShare.ReadWrite来避免锁
  • 个性化您的异常消息
  • 对于Windows
< p > # EYZ0
ExceptionToString()是一个个人扩展,试图获得内部异常和显示堆栈。将其替换为ex.Message或任何其他代码。
log4net.ILog _log我使用==Log4net==你可以用不同的方式制作你的日志
/// <summary>
/// Recursive Directory Copy
/// </summary>
/// <param name="fromPath"></param>
/// <param name="toPath"></param>
/// <param name="continueOnException">on error, continue to copy next file</param>
/// <param name="skipHiddenFiles">To avoid files like thumbs.db</param>
/// <param name="skipByModifiedDate">Does not copy if the destiny file has the same or more recent modified date</param>
/// <remarks>
/// </remarks>
public static void CopyEntireDirectory(string fromPath, string toPath, bool continueOnException = false, bool skipHiddenFiles = true, bool skipByModifiedDate = true)
{
log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
string nl = Environment.NewLine;


string sourcePath = "";
string destPath = "";
string _exMsg = "";


void TreateException(Exception ex)
{
_log.Warn(_exMsg);
if (continueOnException == false)
{
throw new Exception($"{_exMsg}{nl}----{nl}{ex.ExceptionToString()}");
}
}


try
{
foreach (string fileName in Directory.GetFileSystemEntries(fromPath, "*", SearchOption.AllDirectories))
{
sourcePath = fileName;
destPath = Regex.Replace(fileName, "^" + Regex.Escape(fromPath), toPath);


Directory.CreateDirectory(Path.GetDirectoryName(destPath));
            

_log.Debug(FileCopyStream(sourcePath, destPath,skipHiddenFiles,skipByModifiedDate));
}
}
// Directory must be less than 148 characters, File must be less than 261 characters
catch (PathTooLongException)
{
throw new Exception($"Both paths must be less than 148 characters:{nl}{sourcePath}{nl}{destPath}");
}
// Not enough disk space. Cancel further copies
catch (IOException ex) when ((ex.HResult & 0xFFFF) == 0x27 || (ex.HResult & 0xFFFF) == 0x70)
{
throw new Exception($"Not enough disk space:{nl}'{toPath}'");
}
// used by another process
catch (IOException ex) when ((uint)ex.HResult == 0x80070020)
{
_exMsg = $"File is being used by another process:{nl}'{destPath}'{nl}{ex.Message}";
TreateException(ex);
}
catch (UnauthorizedAccessException ex)
{
_exMsg = $"Unauthorized Access Exception:{nl}from:'{sourcePath}'{nl}to:{destPath}";
TreateException(ex);
}
catch (Exception ex)
{
_exMsg = $"from:'{sourcePath}'{nl}to:{destPath}";
TreateException(ex);
}
}


/// <summary>
/// File Copy using Stream 64K and trying to avoid locks with fileshare
/// </summary>
/// <param name="sourcePath"></param>
/// <param name="destPath"></param>
/// <param name="skipHiddenFiles">To avoid files like thumbs.db</param>
/// <param name="skipByModifiedDate">Does not copy if the destiny file has the same or more recent modified date</param>
public static string FileCopyStream(string sourcePath, string destPath, bool skipHiddenFiles = true, bool skipByModifiedDate = true)
{
// Buffer should be 64K = 65536‬ bytes
// Increasing the buffer size beyond 64k will not help in any circunstance,
// as the underlying SMB protocol does not support buffer lengths beyond 64k."
byte[] buffer = new byte[65536];


if (!File.Exists(sourcePath))
return $"is not a file: '{sourcePath}'";


FileInfo sourcefileInfo = new FileInfo(sourcePath);
FileInfo destFileInfo = null;
if (File.Exists(destPath))
destFileInfo = new FileInfo(destPath);


if (skipHiddenFiles)
{
if (sourcefileInfo.Attributes.HasFlag(FileAttributes.Hidden))
return $"Hidden File Not Copied: '{sourcePath}'";
}


using (FileStream input = sourcefileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (FileStream output = new FileStream(destPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite, buffer.Length))
{
if (skipByModifiedDate && destFileInfo != null)
{
if (destFileInfo.LastWriteTime < sourcefileInfo.LastWriteTime)
{
input.CopyTo(output, buffer.Length);
destFileInfo.LastWriteTime = sourcefileInfo.LastWriteTime;
return $"Replaced: '{sourcePath}'";
}
else
{
return $"NOT replaced (more recent or same file): '{sourcePath}'";
}
}
else
{
input.CopyTo(output, buffer.Length);
destFileInfo = new FileInfo(destPath);
destFileInfo.LastWriteTime = sourcefileInfo.LastWriteTime;
return $"New File: '{sourcePath}'";
}
}
}

对于UWP和Winui 3 (WindowsAppSdk)使用异步 API:

public async Task CopyAsync(StorageFolder source, StorageFolder dest)
{
foreach (var item in await source.GetItemsAsync())


if (item is StorageFile file)
await file.CopyAsync(dest);


else if (item is StorageFolder folder)
await CopyAsync(folder, await dest.CreateFolderAsync(folder.Name, CreationCollisionOption.OpenIfExists));
}