在C#中复制目录的全部内容

Kei*_*ith 497 .net c# copy

我想在C#中将目录的全部内容从一个位置复制到另一个位置.

似乎没有办法使用System.IO没有大量递归的类来完成此操作.

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

new Microsoft.VisualBasic.Devices.Computer().
    FileSystem.CopyDirectory( sourceFolder, outputFolder );
Run Code Online (Sandbox Code Playgroud)

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

小智 529

更容易

//Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(SourcePath, "*", 
    SearchOption.AllDirectories))
    Directory.CreateDirectory(dirPath.Replace(SourcePath, DestinationPath));

//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, DestinationPath), true);
Run Code Online (Sandbox Code Playgroud)

  • 为什么`*.*`而不是`*`?你不想复制没有扩展名的文件吗? (33认同)
  • @Xaisoft - 如果在路径中有重复模式,`Replace`有问题,例如``sourceDir/things/sourceDir/things``应该变成`"destinationDir/things/sourceDir/things"`,但是如果你使用替换它成为`"destinationDir/things/destinationDir/things"` (28认同)
  • 它确实是一段很好的代码,但这不是可以在任何地方使用的代码.开发人员应该小心,因为dirPath.Replace可能会导致不必要的后果.只是警告那些喜欢在网上复制和粘贴的人.@jaysponsored发布的代码更安全,因为它不使用string.Replace但我确信它也有它的极端情况. (25认同)
  • 请注意此代码,因为如果目标目录已存在,它将引发异常.它也不会覆盖已存在的文件.只需在创建每个目录之前添加一个检查,并使用File.Copy的重载覆盖目标文件(如果存在). (17认同)
  • 让我们构建一些东西并将其贡献给开源.NET核心......:/ (9认同)
  • 还应注意路径末尾需要反斜杠。如果没有,文件和文件夹名称将附加到每个项目的目标根名称。只是一个提示! (2认同)
  • @AaronLS,“ AllDirectories”选项表示文件可能位于源目录的子目录中。使用您的代码将导致所有文件都复制到目标目录本身,而不是目标目录的子目录中。 (2认同)
  • 这在 Linux 上对我不起作用。只需使用 Microsoft 在 [https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories](https://learn.microsoft.com/en) 概述的递归示例-us/dotnet/standard/io/如何复制目录) (2认同)

Kon*_*lph 222

嗯,我想我误解了这个问题,但我会冒风险.以下简单方法有什么问题?

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));
}
Run Code Online (Sandbox Code Playgroud)

编辑由于这篇帖子已经为这样一个简单的答案提供了大量的downvotes同样简单的问题,让我添加一个解释. 在downvoting之前阅读此内容.

首先,这段代码不是故意替代问题中的代码.它仅用于说明目的.

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

也就是说,代码运行良好.它已经(几乎相同)已经在一个成熟的软件使用多年.除了所有IO处理中存在的固有的浮躁(例如,如果用户在代码写入时手动拔出USB驱动器会发生什么情况?),没有已知的问题.

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

请注意,恶意用户可能会通过使用每个字母的深层嵌套目录来打破此假设.我没试过这个.但只是为了说明这一点:为了使这个代码在典型的计算机上溢出,这些目录必须嵌套几千次.这根本不是一个现实的场景.

  • @DTashkinov:请原谅,但这似乎有点过分.为什么显而易见的代码== downvote?相反应该是真的.已经发布了内置方法,但Keith特别要求*另一种方法.另外,你的最后一句话是什么意思?对不起,但我根本不理解你的原因. (48认同)
  • 直到最近,目录嵌套深度才被操作系统限制.我怀疑你会发现嵌套的目录超过几百次(如果是偶数的话).上面的代码可以花费*多*. (17认同)
  • 这是头递归.如果目录嵌套得足够深,它可能成为堆栈溢出的牺牲品. (5认同)
  • @AMissico:比什么*更好?没有人声称它比框架中的VB代码更好.我们*知道*它不是. (5认同)
  • 我喜欢递归方法,堆栈溢出的风险最小. (4认同)
  • 似乎是一种合理的方法*IF*您不想使用问题中提到的VB代码.我不明白对递归的反对意见.该代码永远不会破坏堆栈,除非您遇到符号链接(也称为重新分析点)回到父目录的情况,并且它很容易避免跟随这些并且还计算递归深度以防止其他奇怪的循环.Microsoft建议在他们的HowTo答案中递归:http://msdn.microsoft.com/en-us/library/vstudio/bb513869%28v=vs.100%29.aspx (4认同)

Jus*_* R. 123

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.
}
Run Code Online (Sandbox Code Playgroud)

  • 没有理由检查目录是否存在,只需调用Directoty.CreateDirectory,如果目录已经存在,它将不执行任何操作. (8认同)
  • 这个答案似乎是所有这些中最有用的。通过使用DirectoryInfo而不是字符串,可以避免很多潜在的问题。 (2认同)

d4n*_*4nt 51

试试这个:

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();
Run Code Online (Sandbox Code Playgroud)

你的xcopy参数可能会有所不同,但你明白了.

  • 对不起,但这太可怕了.它假定目标系统是Windows.它假定未来版本在该特定路径中包含xcopy.exe.它假定xcopy的参数不会改变.它需要将xcopy的参数组合为字符串,这会带来很多错误的可能性.此外,该示例未提及启动过程的结果的任何错误处理,这是我所期望的,因为与其他方法相反,这将无声地失败. (15认同)
  • 添加双引号是安全的. (6认同)
  • 添加/ Y以防止提示覆盖现有文件.http://stackoverflow.com/q/191209/138938 (6认同)
  • / E告诉它复制所有子目录(甚至是空目录)./我告诉它如果目的地不存在,则创建一个具有该名称的目录. (3认同)
  • @MatthiasJansen,我觉得你非常个人化.答案是重点并解释了如何实现它......由于问题不要求跨平台兼容性或不使用xcopy或其他任何东西,海报刚回答解释如何通过单向实现...可能有1000种方法可以做同样的事情,答案各不相同..这就是为什么这个论坛在这里解决,全球的程序员来这里分享他们的经验.我投票给你的评论. (3认同)
  • 添加/ d选项对于仅复制已修改的文件以及/ i选项非常有用 (2认同)

Jos*_*sef 42

或者,如果您想要努力,请为Microsoft.VisualBasic添加对项目的引用,然后使用以下命令:

Microsoft.VisualBasic.FileIO.FileSystem.CopyDirectory(fromDirectory, toDirectory);
Run Code Online (Sandbox Code Playgroud)

但是,使用其中一个递归函数是一种更好的方法,因为它不必加载VB dll.

  • 加载VB组件昂贵吗?VB选项比C#版本更优雅. (9认同)
  • 什么是"VB的向后兼容性东西"?CopyDirectory使用Shell或Framework. (3认同)
  • 我希望它在`System.IO.Directory`上,但它比重写它更好! (3认同)
  • 这是去imo的方式,比其他任何选择都容易 (2认同)

jay*_*red 37

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

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

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)));
    }
Run Code Online (Sandbox Code Playgroud)

  • 伙计们,使用`Path.Combine()`.切勿使用字符串连接将文件路径放在一起. (23认同)
  • 您在上面的代码段中有一个OBOB.你应该使用`source_dir.Length + 1`,而不是`source_dir.Length`. (3认同)

Jen*_*und 14

递归复制文件夹而不进行递归以避免堆栈溢出.

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 很难想象在发光路径限制之前吹掉堆栈 (2认同)

小智 5

这是我用于此类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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)