Скопируйте все содержимое каталога в C #

525

Я хочу скопировать все содержимое каталога из одного места в другое в C #.

Кажется, нет способа сделать это с помощью System.IOклассов без большого количества рекурсии.

В VB есть метод, который мы можем использовать, если добавим ссылку на Microsoft.VisualBasic:

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

Это похоже на довольно уродливый хак. Есть ли способ лучше?

Кит
источник
101
Я бы сказал, что, глядя на альтернативы, опубликованные ниже, путь VB выглядит не так безобразно.
Кевин Кершоу
41
Как это может быть взломом, когда он является частью .NET Framework? Прекратите писать код и используйте то, что у вас есть.
AMissico
15
Это распространенное заблуждение. Microsft.VisualBasic содержит все распространенные процедуры Visual Basic, которые значительно упрощают кодирование в VB. Microsot.VisualBasic.Compatibility - это сборка, используемая для наследства VB6.
AMissico
63
В Microsoft.VisualBasic.Devices.Computer.FileSystem содержится более 2000 строк кода. CopyDirectory гарантирует, что вы не копируете родительскую папку в дочернюю папку и другие проверки. Это высоко оптимизировано, и так далее. Выбранный ответ - в лучшем случае хрупкий код.
AMissico
17
@AMissico - хорошо, так почему это оптимизированное и полный код в Microsoft.VisualBasicи не System.IO? Причина в том, что в Mono этого нет, в том, что все библиотеки, которые считаются «основными», System.[something]- все остальные - нет. У меня нет проблем со ссылками на дополнительную DLL, но есть веская причина, почему Microsoft не включила эту функцию в System.IO.
Кит

Ответы:

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);
tboswell
источник
25
Это действительно хороший кусок кода, но это не тот код, который можно использовать где угодно. Разработчики должны быть осторожны, потому что dirPath.Replace может привести к нежелательным последствиям. Просто предупреждение людям, которые любят копировать и вставлять по сети. Код, размещенный @jaysponsored, безопаснее, потому что он не использует string.Replace, но я уверен, что он также имеет свои угловые случаи.
Алекс
17
Будьте осторожны с этим кодом, так как он вызовет исключение, если целевой каталог уже существует. Он также не будет перезаписывать файлы, которые уже существуют. Просто добавьте проверку перед созданием каждого каталога и используйте перегрузку File.Copy для перезаписи целевого файла, если он существует.
Joerage
30
@Xaisoft - Replaceимеет проблему, если у вас есть повторяющийся шаблон внутри пути, например, "sourceDir/things/sourceDir/things"должен стать "destinationDir/things/sourceDir/things", но если вы используете замену, он становится"destinationDir/things/destinationDir/things"
Кит
35
Почему *.*вместо *? Вы тоже не хотите копировать файлы без расширений?
Дэрил
10
Давайте что-нибудь
создадим и добавим
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выполняет некоторые дополнительные тесты на корректность (например, являются ли источник и цель действительными каталогами, является ли источник родителем цели и т. д.), которые отсутствуют в этом ответе. Этот код, вероятно, также более оптимизирован.

Тем не менее, код работает хорошо . Он имеет (почти одинаково) был использован в зрелом программном обеспечении в течение многих лет. Помимо врожденной неустойчивости, присутствующей во всех обработках ввода-вывода (например, что произойдет, если пользователь вручную отключит USB-накопитель во время записи на него кода?), Нет известных проблем.

В частности, я хотел бы отметить, что использование рекурсии здесь абсолютно не проблема. Ни в теории (концептуально, это самое элегантное решение), ни на практике: этот код не будет переполнять стек . Стек достаточно большой, чтобы обрабатывать даже глубоко вложенные файловые иерархии. Задолго до того, как место в стеке станет проблемой, начинает действовать ограничение длины пути к папке.

Обратите внимание, что злонамеренный пользователь может нарушить это предположение, используя глубоко вложенные каталоги по одной букве в каждой. Я не пробовал это. Но просто чтобы проиллюстрировать это: для того, чтобы код переполнялся на типичном компьютере, каталоги должны быть вложены несколько тысяч раз. Это просто нереальный сценарий.

Конрад Рудольф
источник
5
Это рекурсия головы. Это может стать причиной переполнения стека, если каталоги вложены достаточно глубоко.
Спулсон
19
До недавнего времени глубина вложенности каталогов была ограничена ОС. Я сомневаюсь, что вы найдете каталоги, которые вложены более нескольких сотен раз (если даже). Приведенный выше код может занять гораздо больше.
Конрад Рудольф
5
Мне нравится рекурсивный подход, риск переполнения стека в худшем случае минимален.
Дэвид Басараб
49
@DTashkinov: хорошо, извините, но это кажется чрезмерным. Почему очевидный код == downvote? Обратное должно быть правдой. Встроенный метод уже был опубликован, но Кит специально попросил другой метод. Кроме того, что вы подразумеваете под своим последним предложением? Извините, но я просто не понимаю ваших причин для голосования.
Конрад Рудольф
6
@AMissico: лучше чем что ? Никто не утверждал, что это лучше, чем код VB из фреймворка. Мы знаем, что это не так.
Конрад Рудольф
132

Скопировано из 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 символов, вы можете использовать пакет Nuget с именем ZetaLongPaths
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 могут отличаться, но вы поняли идею.

d4nt
источник
3
/ E говорит ему скопировать все подкаталоги (даже пустые). / Я говорю, что если место назначения не существует, создайте каталог с таким именем.
2010 года
6
добавьте двойную кавычку, чтобы быть в безопасности.
Jaysonragasa
6
Добавьте / Y, чтобы запретить перезаписывать существующие файлы. stackoverflow.com/q/191209/138938
Джон Кроуэлл
16
Извините, но это ужасно. Предполагается, что целевой системой являются окна. Предполагается, что будущие версии будут включать xcopy.exe по указанному пути. Предполагается, что параметры xcopy не меняются. Требуется собрать параметры для xcopy в виде строки, что вносит большой потенциал ошибок. Также в примере не упоминается какая-либо обработка ошибок для результатов запущенного процесса, чего я и ожидал, потому что в отличие от других методов это молча было бы неудачным.
Целый
3
@MatthiasJansen, я думаю, вы восприняли это как личное. Ответ к сути и многое объясняет о том, как этого достичь ... Поскольку этот вопрос не требует кросс-платформенной совместимости или не использует xcopy или что-либо еще, автор просто ответил, чтобы объяснить, как этого можно достичь одним способом ... Там может быть 1000 способов сделать одно и то же, и ответы могут быть разными. Вот почему этот форум здесь, и программисты по всему миру приезжают сюда, чтобы поделиться своим опытом. Я голосую за ваш комментарий.
KMX
48

Или, если вы хотите пойти трудным путем, добавьте ссылку на ваш проект для Microsoft.VisualBasic, а затем используйте следующее:

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

Тем не менее, использование одной из рекурсивных функций является лучшим способом, так как он не должен загружать VB DLL.

Josef
источник
1
В любом случае это не сильно отличается от того, как я это делал - вам все равно нужно загрузить материал обратной совместимости VB, чтобы иметь возможность это сделать.
Кит
10
Дорого ли загружается сборка VB? Варианты VB намного более элегантны, чем версии C #.
jwmiller5
3
Что такое "материал обратной совместимости VB"? CopyDirectory использует либо Shell, либо Framework.
AMissico
3
Хотелось бы, чтобы это было включено System.IO.Directory, но это лучше, чем переписать это!
Джош М.
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)));
    }
jaysponsored
источник
1
Помните о конце обратной косой черты
Алексей F
24
Народ, пользуйтесь Path.Combine(). Никогда не используйте конкатенацию строк для объединения путей к файлам.
Энди
3
У вас есть OBOB в приведенном выше фрагменте кода. Вы должны использовать source_dir.Length + 1, а не source_dir.Length.
PellucidWombat
Этот код является хорошей концепцией, но ... Файл не должен иметь "." в нем, так что было бы лучше использовать ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories))
Жан Либера
Спасибо @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
Трудно себе представить, как взорвать стопку до того, как светится предел пути
Эд С.
5

Вот вспомогательный класс, который я использовал для таких задач ввода-вывода.

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

Возможно, он не учитывает производительность, но я использую его для папок размером 30 МБ, и он работает безупречно. Кроме того, мне не понравился весь объем кода и рекурсии, необходимых для такой простой задачи.

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.

AlexanderD
источник
1
Я тоже, следовательно, вопрос, но выбранный ответ не нуждается в рекурсии. Этот ответ создает zip-файл на диске, что является большой дополнительной работой для копии файла - вы не только создаете дополнительную копию данных, но и тратите процессорное время, сжимая и распаковывая его. Я уверен, что это работает, точно так же, как вы, вероятно, можете забить гвоздь своей обувью, но это больше работа с большим количеством вещей, которые могут пойти не так, в то время как есть лучшие способы сделать это.
Кит
Причина, по которой я закончил с этим, - замена строк. Как уже отмечали другие, принятый ответ вызывает много проблем; Соединительная ссылка может не работать, а также повторять шаблон папки или файлы без расширения или имени. Меньше кода, меньше шансов ошибиться. А поскольку время процессора меня не беспокоит, оно делает его подходящим для моего конкретного случая
AlexanderD
2
Да, это все равно что проехать 1000 миль, чтобы избежать единственного светофора, но это ваше путешествие, так что дерзайте. Проверка шаблонов папок тривиальна по сравнению с тем, что ZIP должен делать под капотом. Я настоятельно рекомендую против этого всем, кто заботится о том, чтобы не тратить впустую процессор, диск, электричество или где это необходимо для запуска вместе с другими программами на той же машине. Кроме того, если вам когда-либо задают этот тип вопроса на собеседовании, никогда не говорите «мой код прост, поэтому мне не нужно время процессора» - вы не получите работу.
Кит
1
Я переключился на ответ, предоставленный @ Джастин-р . Тем не менее, я оставлю этот ответ как еще один способ сделать это
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;
    }
Khoi_Vjz_Boy
источник
См выбранный ответ, используя SearchOptionфлаг в поисках папок и файлов, он делает это в 4 строки кода. Также проверьте .HasFlagрасширение сейчас на enums.
Кит
2

Если вам нравится популярный ответ Конрада, но вы хотите, чтобы 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;
}
toddmo
источник
2

Вы всегда можете использовать это , взято с веб-сайта Microsoft.

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);
        }
    }
}
IATO
источник
1
Это здорово - имейте в виду, что в строке file.CopyTo(temppath, false);написано «скопируйте этот файл в это место, только если он не существует», что в большинстве случаев не то, что мы хотим. Но я могу понять, почему это по умолчанию. Возможно, добавьте флаг в метод для перезаписи файлов.
Энди
2

Замена Proof версии tboswell (которая устойчива к повторяющемуся шаблону в filepath)

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);
    }
bh_earth0
источник
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);
Кит
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);
}
Termininja
источник
Если вы собираетесь использовать 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;
        }
    }
malballah
источник
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);
            }
    }
Lakmal
источник
Приветствия за ответ, но я не уверен, что это добавляет. Тоже 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));
Arash.Zandi
источник
уже были опубликованы некоторые ответы ниже: stackoverflow.com/a/45199038/1951524
Мартин Шнайдер
Спасибо @ MA-Maddin, но копирует ли сама исходная папка? или только содержимое?
Араш.Занди