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

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

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

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

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


当前回答

这段代码的属性:

没有并行任务,性能较差,但其思想是逐文件处理,因此可以记录或停止。 可以跳过隐藏文件 可以跳过修改日期吗 可以打破或不(您选择)对文件复制错误 SMB和FileShare使用64K的Buffer。读写避免锁 个性化您的异常消息 对于Windows

笔记 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}'";
        }
    }
}

其他回答

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

    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的扩展方法。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;
}

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

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

复制自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.
}

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