C#中是否有更快的方式提升目录及内含文件的复制速度?
你当前复制慢的核心原因和递归逻辑无关,问题出在默认的FileInfo.CopyTo实现用了极小的读写缓冲区,针对SMB网络路径没有做专项优化,加上单线程串行复制时每次文件读写都产生大量网络往返开销,才会导致409MB的文件复制耗时超过10分钟。
先修复现有代码的基础问题
你现有代码有个字符串拼接bug:异常提示里的$"Source directory not found:dir.FullName}"少了左大括号,正确写法是$"Source directory not found: {dir.FullName}";另外CopyTo方法默认遇到同名目标文件会直接抛错中断,需要加上第二个参数true启用覆写,避免复制中途失败。替换默认复制方法为Windows原生API
托管层的File.Copy/CopyTo对网络路径的优化很差,直接调用内核层的CopyFileExAPI可以获得数倍的性能提升:这个API内置了大缓冲区、异步预读、SMB协议适配,还可以通过COPY_FILE_NO_BUFFERING标志关闭不必要的系统缓存,进一步降低大文件复制的延迟。using System; using System.IO; using System.Runtime.InteropServices; public static class NativeFileCopy { [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CopyFileEx( string lpExistingFileName, string lpNewFileName, CopyProgressRoutine lpProgressRoutine, IntPtr lpData, ref Int32 pbCancel, CopyFileFlags dwCopyFlags); private delegate CopyProgressResult CopyProgressRoutine( long TotalFileSize, long TotalBytesTransferred, long StreamSize, long StreamBytesTransferred, uint dwStreamNumber, CopyProgressCallbackReason dwCallbackReason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData); private enum CopyProgressResult : uint { PROGRESS_CONTINUE = 0, PROGRESS_CANCEL = 1, PROGRESS_STOP = 2, PROGRESS_QUIET = 3 } private enum CopyProgressCallbackReason : uint { CALLBACK_CHUNK_FINISHED = 0x00000000, CALLBACK_STREAM_SWITCH = 0x00000001 } [Flags] private enum CopyFileFlags : uint { COPY_FILE_FAIL_IF_EXISTS = 0x00000001, COPY_FILE_RESTARTABLE = 0x00000002, COPY_FILE_NO_BUFFERING = 0x00001000 } public static void Copy(string source, string destination, bool overwrite = true) { int cancel = 0; CopyFileFlags flags = overwrite ? 0 : CopyFileFlags.COPY_FILE_FAIL_IF_EXISTS; // 大文件/网络场景保留NO_BUFFERING,小文件多可删除这行启用系统缓存 flags |= CopyFileFlags.COPY_FILE_NO_BUFFERING; bool result = CopyFileEx(source, destination, null, IntPtr.Zero, ref cancel, flags); if (!result) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } } }把原来循环里的
file.CopyTo(targetFilePath)替换成NativeFileCopy.Copy(file.FullName, targetFilePath, true),这一步改完大部分场景下复制耗时能降到1分钟以内。减少不必要的网络请求,用可控并行复制
你原来的实现里,每次创建DirectoryInfo/FileInfo对象、调用GetDirectories/GetFiles都会触发一次网络往返拿元数据,积少成多会浪费大量时间。优化方式是一次性枚举所有源路径下的目录和文件,先批量创建完所有目标目录,再用2-3的并行度复制文件(网络IO场景不要开太高并行度,超过4之后会因为SMB连接数限制、带宽抢占反而变慢)。下面这段代码可以直接替换你原来的整个递归复制逻辑:void CopyDirectoryAndFiles(string sourceDirectory, string destinationDirectory, bool recursive) { if (!Directory.Exists(sourceDirectory)) throw new DirectoryNotFoundException($"Source directory not found: {sourceDirectory}"); Directory.CreateDirectory(destinationDirectory); if (!recursive) return; // 一次性枚举所有目录,提前创建,减少网络往返 var allDirs = Directory.EnumerateDirectories(sourceDirectory, "*", new EnumerationOptions { RecurseSubdirectories = true, AttributesToSkip = FileAttributes.Hidden | FileAttributes.System }); foreach (var dir in allDirs) { var relativePath = Path.GetRelativePath(sourceDirectory, dir); Directory.CreateDirectory(Path.Combine(destinationDirectory, relativePath)); } // 可控并行度复制文件 var allFiles = Directory.EnumerateFiles(sourceDirectory, "*", new EnumerationOptions { RecurseSubdirectories = true, AttributesToSkip = FileAttributes.Hidden | FileAttributes.System }); var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 3 }; Parallel.ForEach(allFiles, parallelOptions, file => { var relativePath = Path.GetRelativePath(sourceDirectory, file); NativeFileCopy.Copy(file, Path.Combine(destinationDirectory, relativePath), true); }); }额外网络路径优化技巧
- 可以提前把UNC网络路径映射为本地盘符再复制,比直接访问
\\服务器名\共享名格式的路径减少10%-20%的协议开销 - 如果你要复制的文件大多是小于1MB的小文件,可以去掉
CopyFileEx里的COPY_FILE_NO_BUFFERING标志,利用系统缓存提升小文件读写速度 - 复制过程中不要额外读取文件属性、做权限校验之类的操作,避免额外的网络请求
- 可以提前把UNC网络路径映射为本地盘符再复制,比直接访问
- 不要自己写FileStream循环读写复制文件,手动控制缓冲区、异步IO的效率很难超过内核原生的CopyFileEx实现
- 不要盲目拉高并行度,网络传输的带宽是固定的,并行太高只会增加协议开销
- 不需要引入第三方文件复制类库,绝大多数这类库内部只是封装了系统原生API,额外加依赖没有意义
内容的提问来源于stack exchange,提问作者Satish

