Скопіюйте весь вміст каталогу в C #


524

Я хочу скопіювати весь вміст каталогу з одного місця в інше в 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);

25
Це дійсно приємний фрагмент коду, але це не той тип коду, який можна використовувати будь-де. Розробники повинні бути обережними, оскільки dirPath.Replace може спричинити небажані наслідки. Просто попередження для людей, які люблять робити копії та вставляти через мережу. Код, опублікований @jaysponsored, більш безпечний, оскільки він не використовує рядок.
Олексій

17
Будьте обережні з цим кодом, оскільки він викине виняток, якщо цільовий каталог вже існує. Він також не перезапише файли, які вже є. Просто додайте чек перед створенням кожного каталогу та використовуйте перевантаження File.Copy, щоб перезаписати цільовий файл, якщо він існує.
веселощі

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

EDIT Оскільки ця публікація набрала вражаючу кількість записів для такої простої відповіді на не менш просте запитання, дозвольте додати пояснення. Будь ласка, прочитайте це перед тим, як відмовитись .

Перш за все, цей код не є наміром, як заміною коду у питанні. Це лише для ілюстрації.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectoryробить деякі додаткові тести на правильність (наприклад, чи джерело та ціль є дійсними каталогами, чи джерело є батьком цілі тощо), які відсутні у цій відповіді. Цей код, ймовірно, також більш оптимізований.

Однак, код працює добре . Він має (майже однаково) був використаний в зрілому програмному забезпеченні в протягом багатьох років. Крім притаманної нестабільності, яка присутня у всіх обробках IO (наприклад, що відбувається, якщо користувач вручну відключить USB-накопичувач, поки ваш код пише на нього?), Немає відомих проблем.

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

Зауважте, що зловмисний користувач міг би зламати це припущення, використовуючи глибоко вкладені каталоги по одній букві кожен. Я цього не пробував. Але лише для того, щоб проілюструвати суть: для того, щоб цей код переповнювався на типовому комп'ютері, каталоги повинні були вкладатись у кілька тисяч разів. Це просто не реалістичний сценарій.


5
Це рекурсія голови. Він може стати здобиччю до переповнення стека, якщо каталоги вкладені досить глибоко.
подружжя

19
Ще зовсім недавно глибина вкладення каталогу обмежувалася ОС. Сумніваюсь, що ви знайдете каталоги, які вкладені більше декількох сотень разів (якщо навіть). Наведений вище код може зайняти набагато більше.
Конрад Рудольф

5
Мені подобається рекурсивний підхід, в гіршому випадку ризик переповнення стека мінімальний.
Давид Басараб

49
@DTashkinov: Добре вибачте, але це здається надмірним. Чому очевидний код == downvote? Має бути навпаки. Вбудований метод вже був розміщений, але Кіт попросив спеціально інший метод. Також, що ви маєте на увазі під своїм останнім реченням? Вибачте, але я взагалі не розумію ваших причин відмови.
Конрад Рудольф

6
@AMissico: краще за що ? Ніхто не стверджував, що він є кращим за код VB з фреймворку. Ми знаємо, що це не так.
Конрад Рудольф

131

Скопійовано з 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 замість рядків, можна уникнути багатьох потенційних проблем.
ДедалАльфа

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 можуть відрізнятися, але ви розумієте.


3
/ E повідомляє йому скопіювати всі підкаталоги (навіть порожні). / Я кажу йому, що якщо призначення не існує, створіть каталог з цим іменем.
d4nt

6
додайте подвійну цитату, щоб бути безпечною.
jaysonragasa

6
Додати / Y, щоб запобігти запиту на перезапис існуючих файлів. stackoverflow.com/q/191209/138938
Джон Кроуелл

16
Вибачте, але це жахливо. Він передбачає, що цільовою системою є windows. Він передбачає, що майбутні версії включають xcopy.exe на цьому конкретному шляху. Він передбачає, що параметри xcopy не змінюються. Для цього потрібно зібрати параметри для xcopy як рядок, що вводить велику кількість помилок. Також зразок не згадує жодних помилок щодо результатів запущеного процесу, на які я б очікував, тому що всупереч іншим методам це мовчки не вдасться.
чел гострий

3
@MatthiasJansen, я думаю, ти сприйняв це дуже особисто. Відповідь суть і пояснює багато про те, як цього досягти ... Оскільки питання не вимагає сумісності між платформами або не використовує xcopy чи що-небудь інше, плакат просто відповів, щоб пояснити, як цього можна досягти одним способом ... Там може бути 1000 способів зробити те ж саме, і відповіді різні. Ось чому цей форум тут, щоб звернутися до програмистів по всьому світу, щоб поділитися своїм досвідом. Я голосую за ваш коментар.
KMX

47

Або, якщо ви хочете піти важким шляхом, додайте посилання на свій проект для Microsoft.VisualBasic і використовуйте наступне:

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

Однак використання однієї з рекурсивних функцій є кращим способом, оскільки не доведеться завантажувати VB dll.


1
Це насправді не відрізняється від того, як я це робив у будь-якому випадку - вам все-таки потрібно завантажити речі, сумісні з VB, для того, щоб мати можливість це зробити.
Кіт

10
Чи завантажувати збірку VB дорого? Параметри VB набагато елегантніші, ніж версії C #.
jwmiller5

3
Що таке "зворотна сумісність VB"? CopyDirectory використовує або оболонку, або рамку.
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)));
    }

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

Ось клас корисності, який я використовував для таких завдань IO.

using System;
using System.Runtime.InteropServices;

namespace MyNameSpace
{
    public class ShellFileOperation
    {
        private static String StringArrayToMultiString(String[] stringArray)
        {
            String multiString = "";

            if (stringArray == null)
                return "";

            for (int i=0 ; i<stringArray.Length ; i++)
                multiString += stringArray[i] + '\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


1
Звідси і я, отже, питання, але обрана відповідь не потребує рекурсії. Ця відповідь створює zip-файл на диску, що є чималою додатковою роботою для копіювання файлу - ви не тільки створюєте додаткову копію даних, але й витрачаєте час процесора на його стискання та декомпресію. Я впевнений, що це працює, так само, як ви, ймовірно, можете забити цвях у взутті, але це більше роботи з більшою кількістю речей, які можуть піти не так, хоча є кращі способи зробити це.
Кіт

Причиною цього я став заміна рядків. Як зазначали інші, прийнята відповідь викликає багато проблем; з'єднання з'єднання може не працювати, як і повторення шаблону папки або файлів без розширення чи імені. Менше коду, менше шансів помилитися. А оскільки час процесора для мене не хвилює, він робить його підходящим для мого конкретного випадку
AlexanderD

2
Так, це як проїхати 1000 миль зі свого шляху, щоб уникнути жодного світлофора, але це ваша подорож, тому йдіть за цим. Перевірка шаблонів папок є тривіальною порівняно з тим, що ZIP потрібно робити під кришкою. Я настійно рекомендую проти цього всім, хто не турбується про те, щоб не витрачати процесор, диск, електрику або там, де це потрібно працювати разом з іншими програмами на тій же машині. Крім того, якщо вам коли-небудь задають такий тип запитання на співбесіді, ніколи не йдіть з "мій код простий, тому я не переймаюся часом процесора" - ви не отримаєте роботу.
Кіт

1
Я перейшов до відповіді, яку надав @ justin-r . І все ж я залишу цю відповідь там як просто інший спосіб зробити це
AlexanderD

1
Якщо папки знаходяться на окремих мережевих спільних ресурсах і містять багато файлів, на мою думку, це був би найкращий варіант.
Danny Parker

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

Перегляньте обрану відповідь, використовуючи 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;
}

2

Ви завжди можете використовувати це , взяте з веб-сайту Microsofts.

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

1
Це чудово - майте на увазі, що у рядку file.CopyTo(temppath, false);написано "скопіюйте цей файл у це місце, лише якщо його не існує", що більшість часу не є тим, що ми хочемо. Але я можу зрозуміти, чому це дефолт. Можливо, додати прапор до способу перезапису файлів.
Енді

2

tboswell замінить 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);
    }

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

EDIT: Зміна @Ahmed Sabry на повну паралельну передбачуваність дає кращий результат, однак код використовує рекурсивну функцію і її не ідеально в деяких ситуаціях.

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

Якщо ви збираєтесь використовувати 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;
        }
    }

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

Ура за відповідь, але я не впевнений, що це додає. Також 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));

вже було опубліковано кілька відповідей нижче: stackoverflow.com/a/45199038/1951524
Мартін Шнайдер

Дякую @ MA-Maddin, але чи копіює вона сама папку-джерело? чи просто вміст?
Араш.Занді
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.