Kopieren Sie den gesamten Inhalt eines Verzeichnisses in C #

524

Ich möchte den gesamten Inhalt eines Verzeichnisses in C # von einem Speicherort an einen anderen kopieren.

Es scheint keine Möglichkeit zu geben, dies mit System.IOKlassen ohne viel Rekursion zu tun .

In VB gibt es eine Methode, die wir verwenden können, wenn wir einen Verweis auf Folgendes hinzufügen Microsoft.VisualBasic:

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

Dies scheint ein ziemlich hässlicher Hack zu sein. Gibt es einen besseren Weg?

Keith
quelle
101
Ich würde sagen, dass der VB-Weg bei Betrachtung der unten aufgeführten Alternativen nicht so hässlich aussieht.
Kevin Kershaw
41
Wie kann es ein Hack sein, wenn es Teil von .NET Framework ist? Hör auf, Code zu schreiben und benutze, was du hast.
AMissico
15
Das ist ein weit verbreitetes Missverständnis. Microsft.VisualBasic enthält alle gängigen Visual Basic-Prozeduren, die das Codieren in VB erheblich vereinfachen. Microsot.VisualBasic.Compatibility ist die Assembly, die für VB6-Legacy verwendet wird.
AMissico
63
Microsoft.VisualBasic.Devices.Computer.FileSystem enthält über 2.000 Codezeilen. CopyDirectory stellt sicher, dass Sie keinen übergeordneten Ordner in einen untergeordneten Ordner und andere Überprüfungen kopieren. Es ist stark optimiert und so weiter. Die ausgewählte Antwort ist bestenfalls fragiler Code.
AMissico
17
@AMissico - ok, warum ist dieser optimierte und vollständige Code in Microsoft.VisualBasicund nicht System.IO? Der Grund, warum es nicht in Mono ist, ist, dass alle Bibliotheken, die als "Kern" betrachtet werden, sind System.[something]- alle anderen nicht. Ich habe kein Problem damit, auf eine zusätzliche DLL zu verweisen, aber es gibt einen guten Grund, warum Microsoft diese Funktion nicht aufgenommen hat System.IO.
Keith

Antworten:

553

Viel einfacher

//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
quelle
25
Es ist in der Tat ein schönes Stück Code, aber dies ist nicht die Art von Code, die überall verwendet werden kann. Entwickler sollten vorsichtig sein, da dirPath.Replace unerwünschte Folgen haben kann. Nur eine Warnung an Leute, die gerne kopieren und über das Internet einfügen. Der von @jaysponsored gepostete Code ist sicherer, da er keine Zeichenfolge verwendet. Ersetzen, aber ich bin sicher, dass er auch Eckfälle hat.
Alex
17
Seien Sie vorsichtig mit diesem Code, da er eine Ausnahme auslöst, wenn das Zielverzeichnis bereits vorhanden ist. Es werden auch keine bereits vorhandenen Dateien überschrieben. Fügen Sie einfach eine Prüfung hinzu, bevor Sie jedes Verzeichnis erstellen, und überschreiben Sie die Zieldatei, falls vorhanden, mithilfe der Überladung von File.Copy.
Joerage
30
@Xaisoft - Replacehat ein Problem, wenn Sie ein sich wiederholendes Muster innerhalb des Pfades haben, sollte zum Beispiel "sourceDir/things/sourceDir/things"werden "destinationDir/things/sourceDir/things", aber wenn Sie ersetzen verwenden, wird es"destinationDir/things/destinationDir/things"
Keith
35
Warum *.*statt *? Möchten Sie keine Dateien auch ohne Erweiterungen kopieren?
Daryl
10
Lassen Sie uns etwas bauen und es zum Open Source .NET Core beitragen ...: /
Mzn
231

Hmm, ich glaube, ich verstehe die Frage falsch, aber ich werde es riskieren. Was ist falsch an der folgenden einfachen Methode?

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

BEARBEITEN Da dieser Beitrag eine beeindruckende Anzahl von Abstimmungen für eine so einfache Antwort auf eine ebenso einfache Frage erhalten hat, möchte ich eine Erklärung hinzufügen. Bitte lesen Sie dies vor dem Downvoting .

Erstens ist dieser Code nicht als Ersatz für den betreffenden Code gedacht. Es dient nur zu Illustrationszwecken.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectoryführt einige zusätzliche Korrektheitstests durch (z. B. ob Quelle und Ziel gültige Verzeichnisse sind, ob die Quelle ein übergeordnetes Element des Ziels ist usw.), die in dieser Antwort fehlen. Dieser Code ist wahrscheinlich auch optimierter.

Das heißt, der Code funktioniert gut . Es wird (fast identisch) seit Jahren in einer ausgereiften Software verwendet. Abgesehen von der inhärenten Unbeständigkeit, die bei allen E / A-Handlings auftritt (z. B. was passiert, wenn der Benutzer das USB-Laufwerk manuell aussteckt, während Ihr Code darauf schreibt?), Sind keine Probleme bekannt.

Insbesondere möchte ich darauf hinweisen, dass die Verwendung der Rekursion hier absolut kein Problem darstellt. Weder theoretisch (konzeptionell ist es die eleganteste Lösung) noch in der Praxis: Dieser Code wird den Stapel nicht überlaufen . Der Stapel ist groß genug, um auch tief verschachtelte Dateihierarchien zu verarbeiten. Lange bevor der Stapelspeicherplatz zum Problem wird, tritt die Begrenzung der Ordnerpfadlänge in Kraft.

Beachten Sie, dass ein böswilliger Benutzer diese Annahme möglicherweise durch die Verwendung tief verschachtelter Verzeichnisse mit jeweils einem Buchstaben brechen kann. Ich habe das nicht versucht. Aber nur um den Punkt zu veranschaulichen: Um diesen Code auf einem typischen Computer überlaufen zu lassen, müssten die Verzeichnisse einige tausend Mal verschachtelt werden . Dies ist einfach kein realistisches Szenario.

Konrad Rudolph
quelle
5
Dies ist eine Kopfrekursion. Es kann einem Stapelüberlauf zum Opfer fallen, wenn die Verzeichnisse tief genug verschachtelt sind.
Spoulson
19
Bis vor kurzem wurde die Tiefe der Verzeichnisverschachtelung durch das Betriebssystem eingeschränkt. Ich bezweifle, dass Sie Verzeichnisse finden, die mehr als ein paar hundert Mal verschachtelt sind (wenn überhaupt). Der obige Code kann viel mehr dauern .
Konrad Rudolph
5
Ich mag den rekursiven Ansatz, das Risiko eines Stapelüberlaufs ist im schlimmsten Fall minimal.
David Basarab
49
@DTashkinov: Entschuldigung, aber das scheint ein bisschen übertrieben. Warum ist offensichtlicher Code == downvote? Das Gegenteil sollte der Fall sein. Die eingebaute Methode war bereits veröffentlicht worden, aber Keith fragte speziell nach einer anderen Methode. Und was meinst du mit deinem letzten Satz? Entschuldigung, aber ich verstehe Ihre Gründe für das Downvoting überhaupt nicht.
Konrad Rudolph
6
@AMissico: besser als was ? Niemand behauptete, es sei besser als der VB-Code aus dem Framework. Wir wissen, dass es nicht so ist.
Konrad Rudolph
132

Von MSDN kopiert :

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.
quelle
8
Es gibt keinen Grund zu überprüfen, ob das Verzeichnis vorhanden ist. Rufen Sie einfach Directoty.CreateDirectory auf, das nichts unternimmt, wenn das Verzeichnis bereits vorhanden ist.
Tal Jerome
1
Für diejenigen, die mit Pfaden mit mehr als 256 Zeichen arbeiten möchten, können Sie ein Nuget-Paket namens ZetaLongPaths
AK
2
Diese Antwort scheint die nützlichste von allen zu sein. Durch die Verwendung von DirectoryInfo anstelle von Zeichenfolgen werden viele potenzielle Probleme vermieden.
DaedalusAlpha
50

Versuche dies:

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

Ihre xcopy-Argumente können variieren, aber Sie haben die Idee.

d4nt
quelle
3
/ E weist es an, alle Unterverzeichnisse (auch leere) zu kopieren. / Ich sage, wenn das Ziel nicht existiert, erstelle ich ein Verzeichnis mit diesem Namen.
d4nt
6
Fügen Sie aus Sicherheitsgründen ein doppeltes Anführungszeichen hinzu.
Jaysonragasa
6
Fügen Sie / Y hinzu, um zu verhindern, dass Sie aufgefordert werden, vorhandene Dateien zu überschreiben. stackoverflow.com/q/191209/138938
Jon Crowell
16
Entschuldigung, aber das ist schrecklich. Es wird davon ausgegangen, dass das Zielsystem Windows ist. Es wird davon ausgegangen, dass zukünftige Versionen xcopy.exe an diesem bestimmten Pfad enthalten. Es wird davon ausgegangen, dass sich die Parameter von xcopy nicht ändern. Es ist erforderlich, die Parameter für xcopy als Zeichenfolge zusammenzustellen, was viel Fehlerpotential mit sich bringt. Das Beispiel erwähnt auch keine Fehlerbehandlung für die Ergebnisse des gestarteten Prozesses, was ich erwarten würde, da dies im Gegensatz zu anderen Methoden stillschweigend fehlschlagen würde.
Cel
3
@MatthiasJansen, ich denke du hast es sehr persönlich genommen. Die Antwort ist auf den Punkt gebracht und erklärt viel darüber, wie dies erreicht werden kann ... Da die Frage keine plattformübergreifende Kompatibilität erfordert oder keine xcopy oder irgendetwas anderes verwendet, hat das Poster nur geantwortet, um zu erklären, wie dies auf eine Weise erreicht werden kann ... Dort Möglicherweise gibt es 1000 Möglichkeiten, dasselbe zu tun, und die Antworten variieren. Deshalb ist dieses Forum hier, um Programmierer auf der ganzen Welt anzusprechen und ihre Erfahrungen auszutauschen. Ich stimme Ihren Kommentar ab.
KMX
47

Wenn Sie den harten Weg gehen möchten, fügen Sie einen Verweis auf Ihr Projekt für Microsoft.VisualBasic hinzu und verwenden Sie dann Folgendes:

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

Die Verwendung einer der rekursiven Funktionen ist jedoch ein besserer Weg, da die VB-DLL nicht geladen werden muss.

Josef
quelle
1
Das unterscheidet sich sowieso nicht wirklich von meiner Vorgehensweise - Sie müssen immer noch die Abwärtskompatibilitätsdaten von VB laden, um dies tun zu können.
Keith
10
Ist das Laden der VB-Baugruppe teuer? Die VB-Optionen sind viel eleganter als die C # -Versionen.
jwmiller5
3
Was für "VBs Abwärtskompatibilitätsmaterial"? CopyDirectory verwendet entweder die Shell oder das Framework.
AMissico
3
Ich wünschte, es wäre eingeschaltet System.IO.Directory, aber es ist besser, als es neu zu schreiben!
Josh M.
2
Dies ist der Weg, um imo zu gehen, viel einfacher als jede der anderen Optionen
Reggaeguitar
38

Diese Seite hat mir immer sehr geholfen, und jetzt bin ich an der Reihe, den anderen mit dem zu helfen, was ich weiß.

Ich hoffe, dass mein Code unten für jemanden nützlich ist.

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
quelle
1
Denken Sie an den nachfolgenden Backslash
Alexey F
24
Leute, benutze Path.Combine(). Verwenden Sie niemals die Verkettung von Zeichenfolgen, um Dateipfade zusammenzustellen.
Andy
3
Sie haben ein OBOB im obigen Code-Snippet. Sie sollten verwenden source_dir.Length + 1, nicht source_dir.Length.
PellucidWombat
Dieser Code ist ein gutes Konzept, aber ... Eine Datei muss kein "." Darin wäre es besser, ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories) zu verwenden
Jean Libera
Danke @JeanLibera, du hast recht. Ich habe den Code mit Ihrem Vorschlag geändert.
Jaysponsored
14

Kopieren Sie den Ordner rekursiv ohne Rekursion, um einen Stapelüberlauf zu vermeiden.

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
quelle
nützliche nicht rekursive Vorlage :)
Minh Nguyen
2
Kaum vorstellbar, den Stapel zu sprengen, bevor die Pfadgrenze überschritten wird
Ed S.
5

Hier ist eine Dienstprogrammklasse, die ich für solche E / A-Aufgaben verwendet habe.

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

quelle
Beachten Sie, dass Microsoft SHFileOperation intern für Microsoft.VisualBasic verwendet.
jrh
3

Es ist möglicherweise nicht leistungsbewusst, aber ich verwende es für 30-MB-Ordner und es funktioniert einwandfrei. Außerdem gefiel mir nicht die gesamte Menge an Code und Rekursion, die für eine so einfache Aufgabe erforderlich war.

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

Hinweis: ZipFile ist unter .NET 4.5+ im System.IO.Compression-Namespace verfügbar

AlexanderD
quelle
1
Ich auch nicht, daher die Frage, aber die ausgewählte Antwort muss nicht rekursiv sein. Diese Antwort erstellt eine Zip-Datei auf der Festplatte. Dies ist eine Menge zusätzlicher Arbeit für eine Dateikopie. Sie erstellen nicht nur eine zusätzliche Kopie der Daten, sondern verbringen auch Prozessorzeit damit, sie zu komprimieren und zu dekomprimieren. Ich bin mir sicher, dass es funktioniert, genauso wie Sie wahrscheinlich einen Nagel mit Ihrem Schuh einschlagen können, aber es ist mehr Arbeit mit mehr Dingen, die schief gehen können, während es bessere Möglichkeiten gibt, dies zu tun.
Keith
Der Grund, warum ich dazu gekommen bin, ist das Ersetzen von Zeichenfolgen. Wie andere betont haben, wirft die akzeptierte Antwort viele Bedenken auf; Junction Link funktioniert möglicherweise nicht und wiederholt Ordnermuster oder Dateien ohne Erweiterung oder Namen. Weniger Code, weniger Fehler. Und da die Prozessorzeit für mich kein Problem darstellt, ist sie für meinen speziellen Fall geeignet
AlexanderD
2
Ja, das ist so, als würden Sie 1000 Meilen aus dem Weg fahren, um einer einzigen Ampel auszuweichen, aber es ist Ihre Reise, also machen Sie es. Das Überprüfen auf Ordnermuster ist trivial im Vergleich zu dem, was ZIP unter der Haube tun muss. Ich würde es jedem empfehlen, der nicht Prozessor, Festplatte oder Strom verschwenden möchte oder wenn dies zusammen mit anderen Programmen auf demselben Computer ausgeführt werden muss. Wenn Ihnen diese Art von Frage beim Vorstellungsgespräch jemals gestellt wird, gehen Sie niemals zu "Mein Code ist einfach, damit mir die Prozessorzeit egal ist" - Sie werden den Job nicht bekommen.
Keith
1
Ich wechselte zu der Antwort von @ justin-r . Trotzdem werde ich diese Antwort dort als eine andere Möglichkeit belassen
AlexanderD
1
Wenn sich die Ordner auf separaten Netzwerkfreigaben befinden und viele Dateien enthalten, ist dies meiner Meinung nach die beste Option.
Danny Parker
2

Eine geringfügige Verbesserung der Antwort von d4nt, da Sie wahrscheinlich nach Fehlern suchen und die xcopy-Pfade nicht ändern müssen, wenn Sie auf einem Server und einem Entwicklungscomputer arbeiten:

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.
quelle
2

Dies ist mein Code, hoffe diese Hilfe

    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
quelle
Zeigen Sie die ausgewählte Antwort an, indem Sie das SearchOptionFlag bei der Suche nach Ordnern und Dateien verwenden. Dies geschieht in 4 Codezeilen. Überprüfen Sie auch die .HasFlagErweiterung jetzt auf Aufzählungen.
Keith
2

Wenn Sie Konrads beliebte Antwort mögen, aber möchten, dass das sourceselbst ein Ordner ist target, anstatt seine untergeordneten Elemente unter den targetOrdner zu legen, finden Sie hier den Code dafür. Es gibt das neu erstellte zurück DirectoryInfo, was praktisch ist:

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
quelle
2

Sie können jederzeit nutzen diese , von Microsofts Website genommen.

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
quelle
1
Das ist großartig - Denken Sie daran, dass in der Zeile file.CopyTo(temppath, false);"Kopieren Sie diese Datei an diesen Ort, nur wenn sie nicht vorhanden ist" steht, was meistens nicht das ist, was wir wollen. Aber ich kann verstehen, warum es standardmäßig so ist. Fügen Sie der Methode zum Überschreiben von Dateien möglicherweise ein Flag hinzu.
Andy
2

tboswell ersetzt die Proof-Version (die gegenüber wiederholten Mustern im Dateipfad unempfindlich ist)

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
quelle
3
Leute, benutze Path.Combine(). Verwenden Sie niemals die Verkettung von Zeichenfolgen, um Dateipfade zusammenzustellen.
Andy
2

Meine Lösung ist im Grunde eine Modifikation der Antwort von @ Termininja, aber ich habe sie ein wenig verbessert und sie scheint mehr als fünfmal schneller zu sein als die akzeptierte Antwort.

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

BEARBEITEN: Das Ändern von @Ahmed Sabry auf vollständig parallele foreach führt zu einem besseren Ergebnis. Der Code verwendet jedoch eine rekursive Funktion und ist in bestimmten Situationen nicht ideal.

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
quelle
1

Entschuldigung für den vorherigen Code, er hatte immer noch Fehler :( (fiel dem schnellsten Waffenproblem zum Opfer). Hier wird er getestet und funktioniert. Der Schlüssel ist SearchOption.AllDirectories, wodurch die Notwendigkeit einer expliziten Rekursion entfällt.

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
quelle
1

Hier ist eine Erweiterungsmethode für DirectoryInfo a la FileInfo.CopyTo (beachten Sie den overwriteParameter):

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
quelle
1

Verwenden Sie diese Klasse.

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
quelle
1
Dies ähnelt anderen Antworten, die für die Verwendung überarbeitet wurden .ToList().ForEach((was etwas mehr Arbeit, Speicher und etwas langsamer ist als nur die direkte Aufzählung der Verzeichnisse) und als Erweiterungsmethode. Die ausgewählte Antwort verwendet SearchOption.AllDirectoriesund vermeidet Rekursion, daher würde ich empfehlen, zu diesem Modell zu wechseln. Auch Sie in der Regel nicht erforderlich , den Namen des Typs in Erweiterungsmethoden - ich es umbenennen würde , CopyTo()so dass es wurdesourceDir.CopyTo(destination);
Keith
1

Eine Variante mit nur einer Schleife zum Kopieren aller Ordner und Dateien:

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
quelle
Wenn Sie verwenden möchten Regex, sollten Sie dies wahrscheinlich auch Regex.Escape(path)als Teil Ihrer Ausdruckskomposition verwenden (insbesondere unter Berücksichtigung des Windows-Pfadtrennzeichens). Sie können auch davon profitieren, ein new Regex()Objekt außerhalb der Schleife zu erstellen (und möglicherweise zu kompilieren) , anstatt sich auf die statische Methode zu verlassen.
jimbobmcgee
0

Besser als jeder Code (Erweiterungsmethode zu DirectoryInfo mit Rekursion)

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
quelle
1
Ich bin mir nicht sicher, was dies über die akzeptierte Antwort hinzufügt, abgesehen von der Verwendung von Rekursion (wo dies nicht erforderlich ist) und dem Ausblenden von Ausnahmen, um das Debuggen zu erschweren.
Keith
0

Kopieren und ersetzen Sie alle Dateien des Ordners

        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
quelle
Prost auf die Antwort, aber ich bin mir nicht sicher, was dies hinzufügt. Auch das try catch throwist sinnlos.
Keith
0

Der folgende Code ist ein Microsoft-Vorschlag zum Kopieren von Verzeichnissen und wird von lieber @iato geteilt, kopiert jedoch nur Unterverzeichnisse und Dateien des Quellordners rekursiv und kopiert den Quellordner nicht selbst (wie Rechtsklick -> Kopieren ).

Unter dieser Antwort gibt es jedoch einen schwierigen Weg :

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

wenn Sie kopieren möchten Inhalt der Quellordner und Unterordner rekursiv können Sie es einfach wie folgt verwenden:

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

Wenn Sie das Quellverzeichnis jedoch selbst kopieren möchten (ähnlich wie Sie mit der rechten Maustaste auf den Quellordner geklickt und dann auf Kopieren geklickt haben und dann im Zielordner auf Einfügen geklickt haben), sollten Sie Folgendes verwenden:

 string source = @"J:\source\";
 string dest= @"J:\destination\";
 DirectoryCopy(source, Path.Combine(dest, new DirectoryInfo(source).Name));
Arash.Zandi
quelle
hat bereits einige Antworten unten gepostet: stackoverflow.com/a/45199038/1951524
Martin Schneider
Danke @ MA-Maddin, aber kopiert es den Quellordner selbst? oder nur der Inhalt?
Arash.Zandi