Copiez l'intégralité du contenu d'un répertoire en C #

524

Je souhaite copier l'intégralité du contenu d'un répertoire d'un emplacement à un autre en C #.

Il ne semble pas y avoir de moyen de le faire en utilisant des System.IOclasses sans beaucoup de récursivité.

Il existe une méthode dans VB que nous pouvons utiliser si nous ajoutons une référence à Microsoft.VisualBasic:

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

Cela semble être un hack plutôt moche. Y a-t-il une meilleure façon?

Keith
la source
101
Je dirais qu'en regardant les alternatives affichées ci-dessous, que la manière VB ne semble pas si moche.
Kevin Kershaw
41
Comment peut-il être un hack quand il fait partie du .NET Framework? Arrêtez d'écrire du code et utilisez ce que vous avez obtenu.
AMissico
15
C'est une idée fausse commune. Microsft.VisualBasic contient toutes les procédures Visual Basic courantes qui facilitent le codage en VB. Microsot.VisualBasic.Compatibility est l'assembly utilisé pour l'héritage VB6.
AMissico
63
Il existe plus de 2 000 lignes de code vers Microsoft.VisualBasic.Devices.Computer.FileSystem. CopyDirectory garantit que vous ne copiez pas un dossier parent dans un dossier enfant et autres vérifications. Il est hautement optimisé, etc. La réponse choisie est au mieux un code fragile.
AMissico
17
@AMissico - ok, alors pourquoi ce code optimisé et complet est-il inclus Microsoft.VisualBasicet non System.IO? La raison pour laquelle il n'est pas dans Mono est parce que toutes les bibliothèques qui sont considérées comme «principales» le sont System.[something]- toutes les autres ne le sont pas. Je n'ai aucun problème à référencer une DLL supplémentaire, mais il y a une bonne raison pour laquelle Microsoft n'a pas inclus cette fonctionnalité System.IO.
Keith

Réponses:

553

Beaucoup plus facile

//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
la source
25
C'est un joli morceau de code en effet mais ce n'est pas le genre de code qui peut être utilisé n'importe où. Les développeurs doivent être prudents car dirPath.Replace peut entraîner des conséquences indésirables. Juste un avertissement pour les gens qui aiment faire du copier-coller sur le net. Le code publié par @jaysponsored est plus sûr car il n'utilise pas de chaîne.Remplacez, mais je suis sûr qu'il a également ses cas d'angle.
Alex
17
Soyez prudent avec ce code car il lèvera une exception si le répertoire cible existe déjà. Il n'écrasera pas non plus les fichiers qui existent déjà. Ajoutez simplement une vérification avant de créer chaque répertoire et utilisez la surcharge de File.Copy pour écraser le fichier cible s'il existe.
joerage
30
@Xaisoft - Replacea un problème si vous avez un motif répétitif à l'intérieur du chemin, par exemple "sourceDir/things/sourceDir/things"devrait le devenir "destinationDir/things/sourceDir/things", mais si vous utilisez le remplacer, il devient"destinationDir/things/destinationDir/things"
Keith
35
Pourquoi *.*au lieu de *? Ne voulez-vous pas aussi copier des fichiers sans extensions?
Daryl
10
Construisons quelque chose et contribuons à l'Open Source .NET Core ...: /
Mzn
231

Hmm, je pense avoir mal compris la question mais je vais la risquer. Quel est le problème avec la méthode simple suivante?

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 Étant donné que cette publication a recueilli un nombre impressionnant de votes négatifs pour une réponse aussi simple à une question tout aussi simple, permettez-moi d'ajouter une explication. Veuillez lire ceci avant de voter .

Tout d'abord, ce code n'est pas destiné à remplacer le code de la question. C'est à des fins d'illustration uniquement.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectoryeffectue des tests de correction supplémentaires (par exemple, si la source et la cible sont des répertoires valides, si la source est un parent de la cible, etc.) qui manquent dans cette réponse. Ce code est probablement aussi plus optimisé.

Cela dit, le code fonctionne bien . Il est (presque à l'identique) utilisé dans un logiciel mature depuis des années. À part l'inconstance inhérente présente avec toutes les manipulations d'E / S (par exemple, que se passe-t-il si l'utilisateur débranche manuellement la clé USB pendant que votre code y écrit?), Il n'y a aucun problème connu.

En particulier, je voudrais souligner que l'utilisation de la récursion ici n'est absolument pas un problème. Ni en théorie (conceptuellement, c'est la solution la plus élégante) ni en pratique: ce code ne débordera pas la pile . La pile est suffisamment grande pour gérer même les hiérarchies de fichiers profondément imbriquées. Bien avant que l'espace de pile ne devienne un problème, la limitation de la longueur du chemin d'accès au dossier entre en jeu.

Notez qu'un utilisateur malveillant pourrait être en mesure de briser cette hypothèse en utilisant des répertoires profondément imbriqués d'une lettre chacun. Je n'ai pas essayé ça. Mais juste pour illustrer le point: afin de faire déborder ce code sur un ordinateur typique, les répertoires devraient être imbriqués quelques milliers de fois. Ce n'est tout simplement pas un scénario réaliste.

Konrad Rudolph
la source
5
C'est la récursion de la tête. Il peut être la proie d'un débordement de pile si les répertoires sont imbriqués suffisamment profondément.
spoulson le
19
Jusqu'à très récemment, la profondeur d'imbrication des répertoires était limitée par le système d'exploitation. Je doute que vous trouverez des répertoires imbriqués plus de quelques centaines de fois (voire même). Le code ci-dessus peut prendre beaucoup plus.
Konrad Rudolph
5
J'aime l'approche récursive, le risque d'un débordement de pile est minimum au pire.
David Basarab
49
@DTashkinov: excusez-moi bien mais cela semble un peu excessif. Pourquoi le code évident == downvote? L'inverse devrait être vrai. La méthode intégrée avait déjà été publiée mais Keith a demandé spécifiquement une autre méthode. Aussi, que voulez-vous dire par votre dernière phrase? Désolé, mais je ne comprends tout simplement pas vos raisons de voter contre.
Konrad Rudolph
6
@AMissico: mieux que quoi ? Personne n'a prétendu qu'il était meilleur que le code VB du framework. Nous savons que ce n'est pas le cas.
Konrad Rudolph
132

Copié à partir de 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.
}
Justin R.
la source
8
Il n'y a aucune raison de vérifier si le répertoire existe, appelez simplement Directoty.CreateDirectory qui ne fera rien si le répertoire existe déjà.
Tal Jerome
1
Pour ceux qui cherchent à gérer des chemins supérieurs à 256 caractères, vous pouvez utiliser un package Nuget appelé ZetaLongPaths
AK
2
Cette réponse semble être la plus utile de toutes. En utilisant DirectoryInfo au lieu de chaînes, de nombreux problèmes potentiels sont évités.
DaedalusAlpha
50

Essaye ça:

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

Vos arguments xcopy peuvent varier mais vous avez l'idée.

d4nt
la source
3
/ E lui dit de copier tous les sous-répertoires (même vides). / Je lui dis que si la destination n'existe pas, créez un répertoire avec ce nom.
d4nt
6
ajouter un guillemet double pour être sûr.
jaysonragasa
6
Ajoutez / Y pour éviter d'être invité à remplacer les fichiers existants. stackoverflow.com/q/191209/138938
Jon Crowell
16
Désolé, mais c'est horrible. Il suppose que le système cible est Windows. Il suppose que les futures versions incluent xcopy.exe à ce chemin spécifique. Il suppose que les paramètres de xcopy ne changent pas. Il nécessite d'assembler les paramètres de xcopy sous forme de chaîne, ce qui introduit beaucoup de potentiel d'erreur. De plus, l'échantillon ne mentionne aucune gestion d'erreur pour les résultats du processus démarré, ce à quoi je m'attendrais, car contrairement à d'autres méthodes, cela échouerait silencieusement.
cel sharp
3
@MatthiasJansen, je pense que vous l'avez prise très personnellement. La réponse est au point et explique beaucoup de choses sur la façon d'y parvenir ... Puisque la question ne demande pas la compatibilité multiplateforme ou ne pas utiliser xcopy ou quoi que ce soit d'autre, l'affiche vient de répondre pour expliquer comment cela peut être réalisé d'une manière ... Là pourrait être 1000 façons de faire la même chose et les réponses varient .. c'est pourquoi ce forum est là pour s'adresser et les programmeurs du monde entier viennent ici pour partager leurs expériences. Je vote contre votre commentaire.
KMX
48

Ou, si vous voulez aller de l'avant, ajoutez une référence à votre projet pour Microsoft.VisualBasic, puis utilisez ce qui suit:

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

Cependant, l'utilisation de l'une des fonctions récursives est une meilleure solution car elle n'aura pas à charger la DLL VB.

Josef
la source
1
Ce n'est pas vraiment différent de la façon dont je l'ai fait de toute façon - vous devez toujours charger les éléments de compatibilité descendante de VB pour pouvoir le faire.
Keith
10
Le chargement de l'ensemble VB coûte-t-il cher? Les options VB sont beaucoup plus élégantes que les versions C #.
jwmiller5
3
Quel "truc de compatibilité descendante de VB"? CopyDirectory utilise le Shell ou le Framework.
AMissico
3
Je souhaite que ce soit activé System.IO.Directory, mais c'est mieux que de le réécrire!
Josh M.
2
C'est la voie à suivre imo, beaucoup plus facile que toutes les autres options
reggaeguitar
38

Ce site m'a toujours beaucoup aidé, et maintenant c'est mon tour d'aider les autres avec ce que je sais.

J'espère que mon code ci-dessous sera utile à quelqu'un.

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
la source
1
Rappelez-vous de la barre oblique inverse
Alexey F
24
Les gens, utilisez Path.Combine(). N'utilisez jamais la concaténation de chaînes pour regrouper les chemins de fichiers.
Andy
3
Vous avez un OBOB dans l'extrait de code ci-dessus. Vous devriez utiliser source_dir.Length + 1, non source_dir.Length.
PellucidWombat
Ce code est un bon concept, mais ... Un fichier n'a pas besoin d'avoir un "." dedans, il serait donc préférable d'utiliser ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories))
Jean Libera
Merci @JeanLibera, vous avez raison. J'ai changé le code avec votre suggestion.
jaysponsored
14

Copiez le dossier récursivement sans récursivité pour éviter un débordement de pile.

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;
    }
}
Jens Granlund
la source
modèle de non-récursivité utile :)
Minh Nguyen
2
Difficile d'imaginer faire exploser la pile avant d'allumer la limite du chemin
Ed S.
5

Voici une classe utilitaire que j'ai utilisée pour des tâches d'E / S comme celle-ci.

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

la source
Notez que Microsoft utilise SHFileOperation en interne pour Microsoft.VisualBasic.
HJR
3

Il n'est peut-être pas sensible aux performances, mais je l'utilise pour des dossiers de 30 Mo et cela fonctionne parfaitement. De plus, je n'ai pas aimé toute la quantité de code et de récursivité nécessaires pour une tâche aussi simple.

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

Remarque: ZipFile est disponible sur .NET 4.5+ dans l'espace de noms System.IO.Compression

AlexanderD
la source
1
Moi non plus, d'où la question, mais la réponse sélectionnée n'a pas besoin de récursivité. Cette réponse crée un fichier zip sur le disque, ce qui représente beaucoup de travail supplémentaire pour une copie de fichier - non seulement vous créez une copie supplémentaire des données, mais vous passez du temps processeur à les compresser et à les décompresser. Je suis sûr que cela fonctionne, de la même manière que vous pouvez probablement enfoncer un clou avec votre chaussure, mais c'est plus de travail avec plus de choses qui peuvent mal tourner, alors qu'il existe de meilleures façons de le faire.
Keith
La raison pour laquelle je me suis retrouvé avec cela est le remplacement des chaînes. Comme d'autres l'ont souligné, la réponse acceptée présente de nombreuses préoccupations; le lien de jonction peut ne pas fonctionner, ainsi que la répétition du modèle de dossier ou des fichiers sans extension ni nom. Moins de code, moins de chance de se tromper. Et comme le temps processeur n'est pas un problème pour moi, il convient à mon cas spécifique
AlexanderD
2
Oui, c'est comme conduire 1000 miles hors de votre chemin pour éviter un seul feu de circulation, mais c'est votre voyage, alors allez-y. La vérification des modèles de dossiers est triviale par rapport à ce que ZIP doit faire sous le capot. Je le déconseille fortement à toute personne qui se soucie de ne pas gaspiller le processeur, le disque, l'électricité ou où cela doit fonctionner avec d'autres programmes sur la même machine. De plus, si vous jamais demandé ce genre de question lors de l' entrevue n'aller avec « mon code est simple , donc je ne me soucie pas de temps processeur » - vous ne serez pas faire le travail.
Keith
1
Je suis passé à la réponse fournie par @ justin-r . Pourtant, je vais laisser cette réponse là comme juste une autre façon de le faire
AlexanderD
1
Si les dossiers se trouvent sur des partages réseau distincts et contiennent beaucoup de fichiers, ce serait la meilleure option à mon avis.
Danny Parker
2

Une amélioration mineure de la réponse de d4nt, car vous voulez probablement vérifier les erreurs et ne pas avoir à changer les chemins xcopy si vous travaillez sur un serveur et une machine de développement:

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));
    }
}
Chris S
la source
2

C'est mon code j'espère que cette aide

    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
la source
Voir la réponse sélectionnée, en utilisant l' SearchOptionindicateur sur les recherches de dossiers et de fichiers, il le fait en 4 lignes de code. Consultez également l' .HasFlagextension maintenant sur les énumérations.
Keith
2

Si vous aimez la réponse populaire de Konrad, mais que vous souhaitez que le sourcedossier lui - même soit sous un dossier target, plutôt que de placer ses enfants sous le targetdossier, voici le code pour cela. Il renvoie le nouveau créé DirectoryInfo, ce qui est pratique:

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
la source
2

Vous pouvez toujours utiliser ce , pris sur le site Web 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);
        }
    }
}
iato
la source
1
C'est génial - Gardez à l'esprit que la ligne file.CopyTo(temppath, false);dit "copiez ce fichier à cet endroit, seulement s'il n'existe pas", ce qui n'est pas la plupart du temps ce que nous voulons. Mais, je peux comprendre pourquoi c'est par défaut. Ajoutez peut-être un indicateur à la méthode de remplacement des fichiers.
Andy
2

la version Proof de remplacement de tboswell (qui résiste à la répétition du modèle dans le chemin de fichier)

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
la source
3
Les gens, utilisez Path.Combine(). N'utilisez jamais la concaténation de chaînes pour regrouper les chemins de fichiers.
Andy
2

Ma solution est essentiellement une modification de la réponse de @ Termininja, mais je l'ai un peu améliorée et elle semble être plus de 5 fois plus rapide que la réponse acceptée.

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: La modification de @Ahmed Sabry en foreach parallèle complet produit un meilleur résultat, cependant le code utilise une fonction récursive et ce n'est pas idéal dans certaines situations.

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));
}
Jerry Liang
la source
1

Désolé pour le code précédent, il y avait encore des bugs :( (tombé en proie au problème de pistolet le plus rapide). Ici, il est testé et fonctionne. La clé est SearchOption.AllDirectories, ce qui élimine le besoin de récursivité explicite.

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);
    }
}
Vinko Vrsalovic
la source
1

Voici une méthode d'extension pour DirectoryInfo à la FileInfo.CopyTo (notez le overwriteparamètre):

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;
}
Daryl
la source
1

Utilisez cette classe.

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);
    }
}
Ahmed Sabry
la source
1
Ceci est similaire à d'autres réponses, refactorisé à utiliser .ToList().ForEach((ce qui est légèrement plus de travail, de mémoire et légèrement plus lent que de simplement énumérer les répertoires directement) et comme méthode d'extension. La réponse sélectionnée utilise SearchOption.AllDirectorieset évite la récursivité, donc je recommanderais de passer à ce modèle. De plus, vous n'avez généralement pas besoin du nom du type dans les méthodes d'extension - je le renommerais pour CopyTo()qu'il deviennesourceDir.CopyTo(destination);
Keith
1

Une variante avec une seule boucle pour copier tous les dossiers et fichiers:

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
la source
Si vous allez utiliser Regex, vous devriez probablement également faire Regex.Escape(path)partie de votre composition d'expression (en particulier compte tenu du séparateur de chemin Windows). Vous pouvez également bénéficier de la création (et peut-être de la compilation) d'un new Regex()objet en dehors de la boucle, plutôt que de vous fier à la méthode statique.
jimbobmcgee
0

Mieux que n'importe quel code (méthode d'extension à DirectoryInfo avec récursivité)

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
la source
1
Je ne sais pas ce que cela ajoute à la réponse acceptée, à part utiliser la récursivité (là où cela n'est pas nécessaire) et masquer les exceptions pour rendre le débogage plus difficile.
Keith
0

Copiez et remplacez tous les fichiers du dossier

        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
la source
Acclamations pour la réponse, mais je ne sais pas ce que cela ajoute. Le try catch throwest également inutile.
Keith
0

Le code ci-dessous est une suggestion de microsoft comment copier des répertoires et il est partagé par cher @iato, mais il copie simplement les sous-répertoires et les fichiers du dossier source de manière récursive et ne copie pas le dossier source lui-même (comme un clic droit -> copier ).

mais il y a un moyen délicat en dessous de cette réponse:

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

si vous voulez copier récursivement le contenu du dossier source et des sous - dossiers, vous pouvez simplement l'utiliser comme ceci:

string source = @"J:\source\";
string dest= @"J:\destination\";
DirectoryCopy(source, dest);

mais si vous souhaitez copier le répertoire source lui-même (similaire au fait que vous avez cliqué avec le bouton droit sur le dossier source et cliqué sur copier, puis dans le dossier de destination sur lequel vous avez cliqué sur coller), vous devez utiliser comme ceci:

 string source = @"J:\source\";
 string dest= @"J:\destination\";
 DirectoryCopy(source, Path.Combine(dest, new DirectoryInfo(source).Name));
Arash.Zandi
la source
a déjà été posté quelques réponses ci-dessous: stackoverflow.com/a/45199038/1951524
Martin Schneider
Merci @ MA-Maddin, mais copie-t-il le dossier source lui-même? ou juste le contenu?
Arash.Zandi