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

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

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

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

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


当前回答

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

其他回答

它可能没有性能意识,但我用它来处理30MB的文件夹,它工作得完美无缺。另外,我不喜欢这么简单的任务所需要的大量代码和递归。

var src = "c:\src";
var dest = "c:\dest";
var cmp = CompressionLevel.NoCompression;
var zip = source_folder + ".zip";

ZipFile.CreateFromDirectory(src, zip, cmp, includeBaseDirectory: false);
ZipFile.ExtractToDirectory(zip, dest_folder);

File.Delete(zip);

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

抱歉之前的代码,它仍然有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);
    }
}

对于UWP和Winui 3 (WindowsAppSdk)使用Async API:

public async Task CopyAsync(StorageFolder source, StorageFolder dest)
{
    foreach (var item in await source.GetItemsAsync())

        if (item is StorageFile file)
            await file.CopyAsync(dest);

        else if (item is StorageFolder folder)
            await CopyAsync(folder, await dest.CreateFolderAsync(folder.Name, CreationCollisionOption.OpenIfExists));
}

这段代码的属性:

没有并行任务,性能较差,但其思想是逐文件处理,因此可以记录或停止。 可以跳过隐藏文件 可以跳过修改日期吗 可以打破或不(您选择)对文件复制错误 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}'";
        }
    }
}
public static class Extensions
{
    public static void Copy(this DirectoryInfo self, DirectoryInfo destination, bool recursively)
    {
        foreach (var file in self.GetFiles())
        {
            file.CopyTo(Path.Combine(destination.FullName, file.Name));
        }

        if (recursively)
        {
            foreach (var directory in self.GetDirectories())
            {
                directory.Copy(destination.CreateSubdirectory(directory.Name), recursively);
            }
        }
    }
}

使用示例:

var sourceDirectory = new DirectoryInfo(@"C:\source");
var destinationDirectory = new DirectoryInfo(@"C:\destination");

if (destinationDirectory.Exists == false)
{
    sourceDirectory.Copy(destinationDirectory, recursively: true);
}