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

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

Похоже, что нет способа сделать это с использованием классов System.IO без большой рекурсии.

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

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

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

Я бы сказал, что, глядя на альтернативы, размещенные ниже, способ VB не выглядит таким уродливым.

Kevin Kershaw 12.09.2008 17:02

Реальный вопрос: почему этого нет в библиотеке ввода-вывода по умолчанию? К настоящему времени мы, вероятно, все поместили один и тот же код в нашу личную библиотеку.

Boris Callens 30.03.2009 15:42

Как это может быть взлом, если он является частью .NET Framework? Прекратите писать код и используйте то, что у вас есть.

AMissico 22.12.2009 06:51

Microsoft.VisualBasic - это набор дополнений для упрощения обновления устаревших проектов VB6. Обычно вы не используете его в приложении C#. Если бы это была «правильная» часть инфраструктуры .Net, она была бы в System.IO. Также только пространства имен System.[something] являются частью Mono.

Keith 22.12.2009 14:01

Это распространенное заблуждение. Microsft.VisualBasic содержит все стандартные процедуры Visual Basic, которые значительно упрощают кодирование на VB. Microsot.VisualBasic.Compatibility - это сборка, используемая для устаревших версий VB6.

AMissico 23.12.2009 23:14

Если вы посмотрите на источник CopyDirectory, вы увидите, что он использует System.IO или внутренние вызовы Shell API, в зависимости от того, как вызывается CopyDirectory.

AMissico 23.12.2009 23:17

Причина, по которой Microsoft.VisualBasic не добавляется в проект C#, заключается в том, что это не проект VB. Даже проекты VB.NET должны добавить Microsoft.VisualBasic.Compatibility, если они хотят использовать унаследованные функции. Уровень совместимости добавляется только мастером миграции или пользователем.

AMissico 23.12.2009 23:19

В Microsoft.VisualBasic.Devices.Computer.FileSystem содержится более 2000 строк кода. CopyDirectory гарантирует, что вы не копируете родительскую папку в дочернюю и другие проверки. Он сильно оптимизирован и так далее. Выбранный ответ - в лучшем случае хрупкий код.

AMissico 23.12.2009 23:26

Это ограничение Mono. Не является частью вашего ответа. Вы хотите лучшего, но его нет.

AMissico 23.12.2009 23:30

Если вам нужен System.Windows.Forms.Design, собираетесь ли вы избегать добавления этой ссылки только потому, что в ней есть слова «Дизайн»? Конечно, нет. Следовательно, избегать чего-то, что встроено в фреймворк, только потому, что в его названии есть слово «VisualBasic», ну ... просто ... глупо.

AMissico 11.01.2010 18:39

Вы, ребята, C#, просто убейте меня. Дело не в инструментах, а в решениях.

AMissico 11.01.2010 18:40

@AMissico - хорошо, тогда почему этот оптимизированный и полный код находится в Microsoft.VisualBasic, а не в System.IO? Причина, по которой его нет в Mono, заключается в том, что все библиотеки, которые считаются «основными», относятся к System.[something], а все остальные - нет. У меня нет проблем со ссылкой на дополнительную DLL, но есть веская причина, по которой Microsoft не включила эту функцию в System.IO.

Keith 12.01.2010 12:11

Всем тем, кто считает, что использование Microsoft.VisualBasic - это нормально: были бы вы счастливы, используя библиотеку из Perl в python? Это в основном то, что происходит, с небольшими отличиями. Кроме того, не используя библиотеки System.*, они потенциально ограничивают себя от использования Mono, что, как я понимаю, может быть проблемой из комментариев, сделанных OP.

RCIX 03.02.2010 05:23

@RCIX: Лучшие разработчики - это те, кто выполняет свою работу эффективно и быстро. Так что, если в имя есть Visual Basic, на самом деле код в DLL - это просто MSIL. Microsoft явно написала эффективный алгоритм, и было бы стыдно, если бы кто-то упустил его из виду только потому, что в его названии есть слово Visual Basic. Тем более, если алгоритм, который он пишет только из-за странности имени DLL, оказывается ошибочным и требует больше времени на исправление.

jasonh 03.02.2010 08:12

@Keith: Возможно, BCL был доработан до того, как они обнаружили, что эта функция нужна. Все, что мы можем сделать, это предположить на этом этапе. У вас есть веская причина, по которой вы не должны включать DLL, кроме имени? В документации MSDN сказано, что эта функция устарела?

jasonh 03.02.2010 08:14

@jasonh: нет - это уже довольно старый вопрос, и ссылка на Microsoft.VisualBasic присутствует в поставляемом программном обеспечении уже более года. Мне просто интересно, почему он находится в странном месте - это должно быть что-то вроде System.IO.Directory.Copy(sourceFolder, outputFolder)

Keith 03.02.2010 14:34

Я согласен с тем, что это должно быть, но определенно правдоподобно, что BCL был доработан до того, как они поняли, что это будет необходимо для VB и, следовательно, его полезности, и они никогда не думали возвращаться к нему. Просто теоретизирую, заметьте. :)

jasonh 05.02.2010 07:31

Я просто ответил на вопросы с некоторыми опциями, подобными xcopy, в C#. stackoverflow.com/questions/22151995/…

Joe Johnston 03.03.2014 20:36

Я использую robocopy, он отлично работает

reggaeguitar 17.01.2015 02:46

Обратите внимание, что Microsoft.VisualBasic.Devices недоступен в ядре .NET: github.com/dotnet/docs/issues/14546

Ohad Schneider 21.08.2020 13:25

Использование этого кода в производственной среде для копирования вложенных каталогов и файлов. Не обнаружил никаких проблем. docs.microsoft.com/en-us/dotnet/standard/io/…

shashwat 15.01.2021 10:14
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
557
22
381 854
22
Перейти к ответу Данный вопрос помечен как решенный

Ответы 22

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

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

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

В любом случае это не сильно отличается от того, как я это делал - вам все равно нужно загрузить материал обратной совместимости VB, чтобы иметь возможность это сделать.

Keith 12.09.2008 16:05

Дорогая ли загрузка сборки VB? Параметры VB намного элегантнее, чем версии C#.

jwmiller5 27.03.2009 22:12

Что за "обратная совместимость VB"? CopyDirectory использует оболочку или платформу.

AMissico 22.12.2009 06:50

Хотелось бы, чтобы это было на System.IO.Directory, но это лучше, чем его переписывать!

Josh M. 05.09.2015 05:11

Это путь, по-моему, намного проще, чем любой другой вариант.

reggaeguitar 30.06.2016 00:20

Это способ! Возможно, нам стоит создать запрос UserVoice или github, чтобы добавить этот код в system.io.directory!

juFo 05.07.2018 11:30

Извините за предыдущий код, в нем все еще были ошибки :( (стал жертвой самой быстрой проблемы с оружием). Здесь он протестирован и работает. Ключ - 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);
    }
}

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

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 12.03.2019 21:06

Хм, думаю, я неправильно понял вопрос, но рискну. Что плохого в следующем простом методе?

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-накопитель, пока ваш код пишет на него?), Известных проблем нет.

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

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

Теперь, кроме рекурсивного вызова. Зачем это нужно делать на C#?

Keith 12.09.2008 16:03

Это головная рекурсия. Он может стать жертвой переполнения стека, если каталоги достаточно глубоко вложены.

spoulson 12.09.2008 16:30

До недавнего времени глубина вложения каталогов ограничивалась ОС. Я сомневаюсь, что вы найдете каталоги, которые вложены более нескольких сотен раз (если даже). Приведенный выше код может занимать много больше.

Konrad Rudolph 12.09.2008 16:55

Мне нравится рекурсивный подход, риск переполнения стека в худшем случае минимален.

David Basarab 12.09.2008 16:58

@DTashkinov: ну извините, но это кажется излишним. Почему очевидный код == отрицательный? Противоположное должно быть правдой. Встроенный метод уже был опубликован, но Кейт специально попросил метод еще один. Кроме того, что вы имеете в виду под своим последним предложением? Извините, но я просто не понимаю ваших причин для голосования против.

Konrad Rudolph 22.07.2009 19:46

Извините, если не понимаете правил голосования за и против. Сам опубликованный код выглядит нормально. Я хотел сказать, что написание собственного кода для тривиальных задач - это первое искушение, которое возникает перед ними, но последнее, что нужно сделать. Лично я посетил эту страницу, пытаясь найти встроенное решение, и был разочарован.

Dmitry Tashkinov 26.11.2009 18:48

Это не лучший способ. Период. Используйте отлаженный и готовый к работе код, который Microsoft предоставила в Framework.

AMissico 24.12.2009 00:25

@AMissico: лучше, чем Какие? Никто не утверждал, что это лучше, чем код VB из фреймворка. У нас знать это не так.

Konrad Rudolph 24.12.2009 18:42

Кажется разумным подходом ЕСЛИ, вы не хотите использовать код VB, упомянутый в вопросе. Я не понимаю возражений против рекурсии. Этот код никогда не взорвет стек, если вы не столкнетесь с ситуацией с символической ссылкой (также известной как точка повторной обработки) на родительский каталог, и достаточно легко избежать этого, а также подсчитать глубину рекурсии для защиты от других странных циклов. Microsoft рекомендует рекурсию в своем ответе HowTo: msdn.microsoft.com/en-us/library/vstudio/…

GrahamS 30.09.2014 00:02

Проголосовали за и добавили отвечать, который является разновидностью этого, где source сам становится папкой в ​​target, а затем его дочерние элементы попадают под это.

toddmo 06.04.2015 01:41

Я не совсем уверен, почему и как, но когда я пытался, код иногда давал сбой, пытаясь скопировать файлы до того, как каталог действительно был создан - мне пришлось добавить Directory.CreateDirectory (target.FullName); ко второму оператору if, чтобы код всегда работал. Любите рекурсивный код, отличное решение

The Lemon 20.07.2020 07:10

@TheLemon В настоящее время код предполагает, что целевой каталог верхнего уровня уже существует. Если код вызван с несуществующей целью, он завершится ошибкой (и добавление проверки для этого может быть хорошей идеей!). Однако подкаталоги созданы правильно: это то, что делает вызов target.CreateSubdirectory.

Konrad Rudolph 20.07.2020 11:39

Попробуй это:

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

что означает / E / I? Перезаписать?

aron 03.03.2010 05:16

/ E указывает ему скопировать все подкаталоги (даже пустые). / I сообщает ему, что если место назначения не существует, создайте каталог с этим именем.

d4nt 03.03.2010 20:22

добавьте двойные кавычки, чтобы быть в безопасности.

jaysonragasa 01.08.2011 13:44

Добавьте / Y, чтобы не получать запрос на перезапись существующих файлов. stackoverflow.com/q/191209/138938

Jon Crowell 23.02.2012 04:13

Добавление параметра / d полезно только для копирования измененных файлов вместе с параметром / i

smirkingman 24.01.2013 14:53

Я знаю, что опаздываю, но я тоже искал решение этой проблемы. Я думаю, у вас тоже должен быть /S, если вы собираетесь использовать /E: «Используйте / e с параметрами командной строки / s и / t» (technet.microsoft.com/en-us/library/bb491035.aspx)

wlyles 12.08.2014 20:43

Если кроссплатформенная поддержка не требуется, это лучший подход. Я бы использовал Робокопия, например, Robocopy C:\A C:\B /E

Ohad Schneider 04.12.2014 09:05

Извините, но это ужасно. Предполагается, что целевая система - это окна. Предполагается, что будущие версии включают xcopy.exe по этому конкретному пути. Предполагается, что параметры xcopy не меняются. Требуется собрать параметры для xcopy в виде строки, что создает большой потенциал ошибок. Также в образце не упоминается какая-либо обработка ошибок для результатов запущенного процесса, чего я ожидал, потому что, в отличие от других методов, это не помогло бы незаметно.

cel sharp 05.09.2016 12:33

@MatthiasJansen, я думаю, ты принял это очень личное. Ответ по существу и многое объясняет о том, как этого добиться ... Поскольку вопрос не требует кросс-платформенной совместимости или использования xcopy или чего-то еще, плакат только что ответил, чтобы объяснить, как этого можно достичь одним способом ... Там может быть 1000 способов сделать то же самое, и ответы на них разные ... вот почему этот форум здесь, чтобы обратиться, и программисты со всего мира приезжают сюда, чтобы поделиться своим опытом. Я голосую против вашего комментария.

KMX 18.10.2016 20:05

Это сработало отлично - спасибо за публикацию. Мне нужно было быстрое и грязное решение для утилиты, которую я пишу. Мне не нужно было много кода.

Matt 07.01.2018 19:05

что лучше или быстрее, xcopy.exe или System.IO?

ewwink 28.05.2018 22:28

Скопировано с 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.
}

Нет причин проверять, существует ли каталог, просто вызовите Directory.CreateDirectory, который ничего не сделает, если каталог уже существует.

Tal Jerome 21.09.2015 18:45

Для тех, кто хочет иметь дело с путями длиной более 256 символов, вы можете использовать пакет Nuget под названием ZetaLongPaths.

A.K 11.05.2016 09:44

Этот ответ кажется самым полезным из всех. Использование DirectoryInfo вместо строк позволяет избежать множества потенциальных проблем.

DaedalusAlpha 12.12.2018 19:13

Скопируйте папку рекурсивно без рекурсии, чтобы избежать переполнения стека.

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 26.04.2018 13:42

Трудно представить, как взорвать стек, прежде чем светится предел пути

Ed S. 19.07.2018 21:34
Ответ принят как подходящий

Намного легче

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

Хорошая идея - не знаю, почему я никогда не думал об использовании SearchOption.AllDirectories. Я бы, вероятно, использовал метод SubString, а не Replace, но это просто стиль кодирования.

Keith 30.09.2010 11:29

Это действительно хороший фрагмент кода, но это не тот код, который можно где угодно использовать. Разработчики должны быть осторожны, потому что dirPath.Replace может вызвать нежелательные последствия. Просто предупреждение для людей, которые любят копировать и вставлять в сети. Код, опубликованный @jaysponsored, безопаснее, потому что он не использует string.Replace, но я уверен, что у него также есть свои угловые случаи.

Alex 03.12.2011 22:58

Потрясающие! Это решит мою проблему. Но есть ли способ копировать / перемещать файлы с миллиардами номеров и без удаления существующих файлов / папок?

Gaurav Aroraa 12.02.2012 13:26

Будьте осторожны с этим кодом, так как он вызовет исключение, если целевой каталог уже существует. Он также не будет перезаписывать уже существующие файлы. Просто добавьте проверку перед созданием каждого каталога и используйте перегрузку File.Copy для перезаписи целевого файла, если он существует.

joerage 15.05.2012 19:02

@Alex - Что в этом случае делает String.Substring лучше String.Replace?

Xaisoft 01.10.2012 18:44

@Alex - именно поэтому я бы использовал метод SubString, а не Replace

Keith 03.10.2012 12:26

@Xaisoft - у Replace есть проблема, если у вас есть повторяющийся шаблон внутри пути, например, "sourceDir/things/sourceDir/things" должен стать "destinationDir/things/sourceDir/things", но если вы используете замену, он станет "destinationDir/things/destinationDir/things"

Keith 03.10.2012 12:35

@Rick этот метод выполняет File.Copy, поэтому он оставляет оригинал. Если вы хотите удалить оригинал, вы можете использовать File.Move или (возможно, лучше) добавить цикл для удаления файлов после завершения копирования.

Keith 03.10.2012 12:37

Почему *.* вместо *? Разве вы не хотите копировать файлы без расширений?

Daryl 27.03.2013 02:43

Следует также отметить, что пути требуют обратной косой черты в конце. В противном случае имена файлов и папок будут добавлены к корневому имени назначения для каждого элемента. Просто совет!

Dave 08.10.2013 17:42

@AaronLS параметр AllDirectories означает, что файлы могут находиться в подкаталогах исходного каталога. Использование вашего кода приведет к тому, что все файлы будут скопированы в сам целевой каталог, а не в подкаталог целевого каталога.

Pete Kirkham 27.05.2014 16:48

@Alex: в чем проблема с dirPath.Replace ??? Не могли бы вы подробнее рассказать о проблемах с этим кодом, предполагая, что пути источника и назначения имеют корень?

Piotr Owsiak 26.08.2014 18:30

Хороший ответ, но одно замечание: разве имя переменной newPath не вводит в заблуждение? Разве его не следует называть скорее sourcePath или sourceFilePath?

Dawid Ohia 03.07.2015 17:06

Что, если нам нужны только файлы из каталогов и подкаталогов. и не хотите каталогов? Кто угодно ?

Adnan Ali 20.08.2015 09:37

@ Вероятно, шансы на это невелики, но я все же согласен с тем, что подстрока - более правильный способ, чем вызывать замену

iliketocode 11.11.2015 05:42

Давайте создадим что-нибудь и внесем это в Open Source .NET Core ...: /

Mzn 11.05.2016 11:03

Это не удается, если каталог содержит какую-либо соединительную ссылку или символическую ссылку. Есть ли способ преодолеть это?

Gökhan Kurt 24.03.2017 09:50

Directory.GetDirectories("*.*", System.IO.SearchOption.AllDirectories) выйдет из строя, если какой-либо из подкаталогов в указанном корне вызовет DirectoryNotFoundException или UnauthorizedAccessException, весь метод завершится ошибкой и не вернет ни одного каталога. Можете ли вы предложить метод, который будет учитывать при возникновении упомянутых выше исключений?

Phathutshedzo Khabubu 10.12.2019 03:15

Этот сайт всегда очень помогал мне, и теперь моя очередь помогать другим тем, что я знаю.

Надеюсь, что приведенный ниже код будет кому-то полезен.

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

Помните о обратной косой черте в конце

Alexey F 16.10.2015 15:06

Ребята, используйте Path.Combine(). Никогда не используйте конкатенацию строк для объединения путей к файлам.

Andy 18.08.2017 15:48

У вас есть OBOB в приведенном выше фрагменте кода. Вы должны использовать source_dir.Length + 1, а не source_dir.Length.

PellucidWombat 14.02.2018 07:07

Этот код - хорошая идея, но ... Файл не обязательно должен иметь "." в нем, поэтому было бы лучше использовать ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories))

Jean Libera 18.05.2018 23:33

Спасибо @JeanLibera, вы правы. Я изменил код с твоим предложением.

eduardomozart 12.06.2018 19:16

замените 'file_name.Substring (..)' на 'Path.GetFileName (file_name)'

dgzornoza 05.07.2018 16:13

Я считаю, что замена @dgzornoza не сработает, потому что функции подстроки удаляют часть source_dir из имени файла, но сохраняют полный путь. Пример: вы копируете файл из D: \ sources \ install.wim в E: \. С функцией подстроки он станет E: \ sources \ install.wim, но если я использую GetFileName, это будет E: \ install.wim. С рекурсией дело обстоит еще хуже.

eduardomozart 05.07.2018 16:24

да, недопустимый GetFileName. Лучшее решение - Substring, заменить предыдущий ответ небезопасно. Спасибо

dgzornoza 06.07.2018 09:43

Лучший ответ, но вы должны использовать source_dir.Length + 1, как упоминалось, или TrimStart('\'))

juanora 04.09.2018 12:58

Отлично. Первоначально мои папки создавались бы с отсутствующей первой буквой. Например: ongs вместо Songs. Затем я удалил косую черту в source_dir = @ "E: \"; и сделал это source_dir = @ "E:" ;. Тогда это сработало в мгновение ока !!!

Venugopal M 05.12.2020 11:32

Незначительное улучшение ответа 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));
    }
}

Это мой код, надеюсь, это поможет

    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 24.09.2012 13:08

Вот метод расширения для 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;
}

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

Версия tboswell replace 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);
    }

Ребята, используйте Path.Combine(). Никогда не используйте конкатенацию строк для объединения путей к файлам.

Andy 18.08.2017 15:51

В моем случае для каталогов мне пришлось использовать Path.Join() вместо Path.Combine(). Я не совсем понимаю, почему, но я предполагаю, что делал что-то, связанное с этим замечанием в документация, который рекомендует Path.Join()

davrob01 10.01.2021 12:03

Вы всегда можете использовать это, взятый с сайта 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);
        }
    }
}

Это здорово - помните, что в строке file.CopyTo(temppath, false); говорится: «скопируйте этот файл в это место, только если он не существует», что в большинстве случаев не то, что нам нужно. Но я могу понять, почему это по умолчанию. Может быть, добавить метку в метод перезаписи файлов.

Andy 18.08.2017 16:42

Используйте этот класс.

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

Это похоже на другие ответы, реорганизованные для использования .ToList().ForEach( (что немного больше работы, памяти и немного медленнее, чем просто перечисление каталогов напрямую) и в качестве метода расширения. Выбранный ответ использует SearchOption.AllDirectories и избегает рекурсии, поэтому я бы рекомендовал перейти на эту модель. Кроме того, обычно вам не нужно имя типа в методах расширения - я бы переименовал его в CopyTo(), чтобы он стал sourceDir.CopyTo(destination);.

Keith 10.08.2017 16:16

Лучше, чем любой код (метод расширения 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;
        }
    }

Я не уверен, что это добавляет к принятому ответу, кроме использования рекурсии (где это не нужно) и сокрытия исключений, чтобы усложнить отладку.

Keith 21.10.2017 00:13

Один вариант с одним циклом для копирования всех папок и файлов:

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 02.11.2019 03:57

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

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.

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

Keith 17.07.2018 12:12

Причина, по которой я пришел к этому, - замена строк. Как отмечали другие, принятый ответ вызывает много опасений; ссылка соединения может не работать, а также повторять шаблон папки или файлы без расширения или имени. Меньше кода, меньше шансов ошибиться. А поскольку время процессора меня не беспокоит, оно подходит для моего конкретного случая.

AlexanderD 17.07.2018 16:16

Да, это все равно что проехать 1000 миль, чтобы избежать светофора, но это ваше путешествие, так что дерзайте. Проверка шаблонов папок тривиальна по сравнению с тем, что ZIP должен делать изнутри. Я настоятельно рекомендую этого не делать всем, кто заботится о том, чтобы не тратить впустую процессор, диск, электричество или где это необходимо, чтобы работать вместе с другими программами на той же машине. Кроме того, если вам когда-нибудь зададут такой вопрос на собеседовании никогда, скажите: «Мой код простой, поэтому мне наплевать на время процессора» - вы не получите работу.

Keith 17.07.2018 18:31

Перешел на ответ предоставил @ justin-r. Тем не менее, я оставлю этот ответ как еще один способ сделать это.

AlexanderD 18.07.2018 12:31

Если папки находятся в разных сетевых папках и содержат много файлов, это, на мой взгляд, лучший вариант.

Danny Parker 22.11.2018 18:52

Очень креативно! С точки зрения правильности это, вероятно, должен быть ответ №1, я уверен, что он обрабатывает 1000 крайних случаев, которые упускают другие наивные ответы. В качестве альтернативы выполните robocopy.exe (если x-plat не проблема).

Ohad Schneider 21.08.2020 13:29

Мое решение в основном является модификацией ответа @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));
}

Скопируйте и замените все файлы в папке

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

Приветствую вас за ответ, но я не уверен, что это добавляет. Также trycatchthrow бессмысленен.

Keith 10.04.2019 09:31

Код ниже - это предложение 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 18.06.2019 17:52

Спасибо @ MA-Maddin, но копирует ли он саму исходную папку? или только содержимое?

Arash.Zandi 19.06.2019 20:45

Другие вопросы по теме