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

时间:2020-03-05 18:52:08  来源:igfitidea点击:

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

似乎没有一种方法可以使用System.IO类进行大量递归。

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

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

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

解决方案

回答

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

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] + '
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));
}
'; multiString += '
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();
'; 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; } } }

回答

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

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

编辑由于此帖子对于同样简单的问题的简单回答而获得了可观的赞誉,所以让我添加一个解释。请在降级投票之前阅读此内容。

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

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

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

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

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

回答

试试这个:

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

xcopy参数可能有所不同,但是我们可以理解。

回答

从MSDN复制:

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

回答

以递归方式复制文件夹而无需递归,以避免堆栈溢出。

##代码##

回答

容易得多

##代码##