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


524

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

似乎没有一种方法可以在System.IO没有大量递归的情况下使用类。

如果添加对以下内容的引用,则VB中可以使用一种方法Microsoft.VisualBasic

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

这似乎是一个非常丑陋的hack。有没有更好的办法?


101
我想说,看看下面发布的替代方案,VB方式看起来并不那么难看。
凯文·克肖

41
如果它是.NET Framework的一部分,怎么可能成为黑客?停止编写代码并使用您所拥有的。
2009年

15
这是一个普遍的误解。Microsft.VisualBasic包含所有常见的Visual Basic过程,从而使在VB中进行编码变得非常容易。Microsot.VisualBasic.Compatibility是用于VB6旧版的程序集。
09年

63
Microsoft.VisualBasic.Devices.Computer.FileSystem有超过2,000行代码。CopyDirectory确保您不会将父文件夹复制到子文件夹中,并且不会进行其他检查。它是高度优化的,依此类推。所选答案充其量是脆弱的代码。
AMissico

17
@AMissico -好了,这是为什么优化和完整代码Microsoft.VisualBasic,而不是System.IO?它不在Mono中的原因是因为所有被认为是“核心”的库都是System.[something]-其他所有库都不是。我毫无疑问地引用了一个额外的DLL,但是有充分的理由说明为什么Microsoft没有在中包含此功能System.IO
基思2010年

Answers:


553

容易得多

//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);

25
确实,这是一段不错的代码,但这不是可以在任何地方使用的那种代码。开发人员应小心,因为dirPath.Replace可能会导致不必要的后果。只是警告那些喜欢在网络上复制和粘贴的人。@jaysponsored发布的代码更安全,因为它不使用string.Replace,但我敢肯定它也有一些极端情况。
亚历克斯

17
请谨慎使用此代码,因为如果目标目录已存在,它将引发异常。它也不会覆盖已经存在的文件。只需在创建每个目录之前添加检查,然后使用File.Copy的重载来覆盖目标文件(如果存在)。
欢呼声,2012年

30
@Xaisoft- Replace如果您在路径内有重复模式,例如"sourceDir/things/sourceDir/things"应该变为"destinationDir/things/sourceDir/things",则出现问题,但是如果使用replace则变为"destinationDir/things/destinationDir/things"
Keith

35
为什么要*.*代替*?您也不想复制没有扩展名的文件吗?
达里尔

10
让我们构建一些东西并将其贡献给开源.NET Core ...:/
Mzn

231

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

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));
}

编辑由于此帖子为同样简单的问题提供了如此简单的答案,因此赢得了不小的赞誉,让我添加一个解释。请先 阅读此内容,然后再投票

首先,此代码无意替代问题中的代码。仅用于说明目的。

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectory进行一些其他正确性测试(例如,源和目标是否为有效目录,源是否为目标的父级,等等),这些答案均不存在。该代码可能也进行了优化。

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

特别要指出的是,在这里使用递归绝对不是问题。从理论上(从概念上讲,这是最优雅的解决方案)或在实践中都没有此代码不会溢出堆栈。堆栈足够大,甚至可以处理深度嵌套的文件层次结构。早在堆栈空间成为问题之前,文件夹路径长度限制就开始起作用。

请注意,恶意用户也许可以通过使用每个字母一个字母的深层目录来打破这一假设。我没有尝试过。但这只是为了说明这一点:为了使此代码在典型的计算机上溢出,目录必须嵌套数千次。这根本不是现实的情况。


5
这是头部递归。如果目录嵌套得足够深,它可能成为堆栈溢出的牺牲品。
spoulson

19
直到最近,目录嵌套深度仍受操作系统的限制。我怀疑您会发现嵌套了数百次以上的目录(即使是偶数)。上面的代码可以采取了。
Konrad Rudolph

5
我喜欢递归方法,最坏的情况是堆栈溢出的风险最小。
David Basarab

49
@DTashkinov:不好意思,但这似乎有点过头了。为什么明显的代码==下注?反之亦然。内置方法已经发布,但是Keith专门要求使用另一种方法。另外,您的最后一句话是什么意思?抱歉,但是我根本不理解您拒绝投票的原因。
康拉德·鲁道夫2009年

6
@AMissico:比什么还好?没有人声称它比框架中的VB代码更好。我们知道不是。
康拉德·鲁道夫

131

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.
}

8
无需检查目录是否存在,只需调用Directoty.CreateDirectory即可,如果目录已存在则不会执行任何操作。
塔尔·杰罗姆

1
对于那些希望处理大于256个字符的路径的用户,可以使用一个名为ZetaLongPaths的Nuget软件包
AK

2
这个答案似乎是所有这些中最有用的。通过使用DirectoryInfo而不是字符串,可以避免很多潜在的问题。
DaedalusAlpha

50

尝试这个:

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参数可能有所不同,但您可以理解。


3
/ E告诉它复制所有子目录(甚至是空目录)。/ I告诉您,如果目的地不存在,请使用该名称创建目录。
d4nt 2010年

6
为了安全起见,请添加双引号。
jaysonragasa 2011年

6
添加/ Y以防止提示您覆盖现有文件。stackoverflow.com/q/191209/138938
乔恩·克罗威尔

16
对不起,但这太可怕了。它假定目标系统是Windows。假定将来的版本在该特定路径中包括xcopy.exe。假定xcopy的参数不变。它要求将xcopy的参数组装为字符串,这会带来大量的潜在错误。同样,该示例没有提到启动过程的结果所引起的任何错误处理,这是我所期望的,因为与其他方法相反,这将导致静默失败。
cel Sharp

3
@MatthiasJansen,我想您是非常个人的。答案是正确的,并解释了如何实现这一目标。。。由于该问题不需要跨平台兼容性,或者不使用xcopy或其他任何东西,因此发帖人只是回答了一下,以解释如何实现此目标。可能是做同一件事的1000种方法,答案也各不相同。这就是该论坛在这里演讲的原因,全球的程序员都在这里分享他们的经验。我不赞成你的评论。
KMX

47

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

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

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


1
无论如何,这与我的做法并没有真正的不同-您仍然需要加载VB的向后兼容的东西才能做到。
Keith

10
加载VB组件是否昂贵?VB选项比C#版本优雅得多。
jwmiller5 2009年

3
什么是“ VB向后兼容的东西”?CopyDirectory使用命令行管理程序或框架。
2009年

3
我确实希望它处于启用状态System.IO.Directory,但是比重写它更好!
乔什M.

2
这是去imo的方式,比其他任何选择都容易得多
reggaeguitar

38

这个站点总是对我有很大帮助,现在轮到我用我所知道的来帮助其他人了。

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

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)));
    }

1
记住后面的反斜杠
Alexey F

24
民间,使用Path.Combine()。切勿使用字符串串联将文件路径放在一起。
安迪

3
上面的代码片段中有一个OBOB。您应该使用source_dir.Length + 1而不是source_dir.Length
PellucidWombat

这段代码是一个很好的概念,但是...文件不必带有“。”。因此,最好使用ystem.IO.Directory.GetFiles(source_dir,“ *”,System.IO.SearchOption.AllDirectories))
Jean Libera,

谢谢@JeanLibera,您是对的。我根据您的建议更改了代码。
jaysponsored

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;
    }
}

有用的非递归模板:)
Minh Nguyen

2
很难想象在达到路径限制之前就将堆栈炸毁
Ed S.

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;
        }
    }
}

请注意,Microsoft内部将SHFileOperation用于Microsoft.VisualBasic。
jrh

3

它可能不具备性能意识,但是我将其用于30MB文件夹,并且可以完美运行。另外,我不喜欢这么简单的任务所需的全部代码和递归。

var source_folder = "c:\src";
var dest_folder = "c:\dest";
var zipFile = source_folder + ".zip";

ZipFile.CreateFromDirectory(source_folder, zipFile);
ZipFile.ExtractToDirectory(zipFile, dest_folder);
File.Delete(zipFile);

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


1
我也不是问题,但是选择的答案不需要递归。这个答案会在磁盘上创建一个zip文件,这对于文件副本来说是很多额外的工作-不仅要创建数据的额外副本,而且还要花费处理器时间来压缩和解压缩数据。我敢肯定,它的工作原理与您可能用鞋砸钉子的方式相同,但是在有更多可能出错的事情上,它还有更多工作要做,同时还有更好的方法可以做到。
基思

我之所以这样做,是因为字符串替换。正如其他人指出的那样,公认的答案引起了许多关注。联结链接可能无效,并且重复文件夹模式或没有扩展名或名称的文件。更少的代码,更少的出错机会。而且由于不需要处理器时间,因此它适合我的具体情况
AlexanderD

2
是的,这就像开着1000英里的路来避开一个交通信号灯,但这是你的旅程,所以去吧。与ZIP需要做的事情相比,检查文件夹模式很简单。对于那些关心不浪费处理器,磁盘,电力或需要与其他程序一起运行在同一台计算机上的人,我强烈建议您这样做。另外,如果在面试中有人问过您这种类型的问题,那就不要说“我的代码很简单,所以我不在乎处理器时间”-您将无法获得工作。
基思

1
我切换到@ justin-r提供答案。不过,我会在那儿离开这个答案,因为这样做的另一种方式
AlexanderD

1
如果文件夹位于单独的网络共享上并且包含很多文件,那么我认为这将是最佳选择。
丹尼·帕克

2

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));
    }
}

2

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

    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;
    }

通过使用SearchOption搜索文件夹和文件上的标志来查看选定的答案,它以4行代码来完成此操作。还要立即检查.HasFlag枚举的扩展名。
Keith

2

如果您喜欢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;
}

2

您始终可以使用Microsoft网站上的this

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);
        }
    }
}

1
太好了-记住这一行file.CopyTo(temppath, false);说“只有在不存在的情况下,将此文件复制到此位置”,这在大多数情况下不是我们想要的。但是,我可以理解为什么默认它。也许在覆盖文件的方法中添加一个标志。
安迪

2

tboswell的替换Proof版本(可抵抗文件路径中的重复模式)

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);
    }

3
民间,使用Path.Combine()。切勿使用字符串串联将文件路径放在一起。
安迪

2

我的解决方案基本上是对@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));
}

1

对不起以前的代码,它仍然存在错误:((让猎物解决最快的枪支问题)。这里已经过测试并且可以正常工作。关键是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);
    }
}

1

这是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;
}

1

使用此类。

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);
    }
}

1
这与其他答案类似,可以重构使用.ToList().ForEach((比直接枚举目录要多一些工作,占用更多内存,并且要慢一些)和作为扩展方法。选择的答案使用SearchOption.AllDirectories并避免了递归,因此我建议切换到该模型。同样,您通常在扩展方法中不需要类型的名称-我将其重命名为,CopyTo()因此它变得sourceDir.CopyTo(destination);
Keith

1

一种变体,只有一个循环可以复制所有文件夹和文件:

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);
}

如果要使用Regex,则可能还应将其Regex.Escape(path)作为表达式组成的一部分(尤其是考虑Windows路径分隔符)。您可能还从创建(也许编译)一个获得利益new Regex()循环的物体之外,而不是依靠静态方法。
jimbobmcgee

0

比任何代码都要好(带有递归的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;
        }
    }

1
除了使用递归(不需要的地方)和隐藏异常以使调试更加困难之外,我不确定这会增加可接受的答案。
基思

0

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

        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);
            }
    }

答案很高兴,但我不确定这会增加什么。也try catch throw没有意义。
基思

0

下面的代码是Microsoft建议的“ 如何复制目录” ,它由亲爱的@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));

已经在下面发布了一些答案:stackoverflow.com/a/45199038/1951524
Martin Schneider

感谢@ MA-Maddin,但它会复制源文件夹本身吗?还是只是内容?
Arash.Zandi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.