Copia l'intero contenuto di una directory in C #


524

Voglio copiare l'intero contenuto di una directory da una posizione all'altra in C #.

Non sembra esserci un modo per farlo usando le System.IOclassi senza molta ricorsione.

Esiste un metodo in VB che possiamo usare se aggiungiamo un riferimento a Microsoft.VisualBasic:

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

Sembra un brutto trucco. Esiste un modo migliore?


101
Direi che guardando le alternative pubblicate qui sotto, che il modo VB non sembra così brutto.
Kevin Kershaw,

41
Come può essere un hack quando fa parte di .NET Framework? Smetti di scrivere codice e usa quello che hai.
AMissico,

15
Questo è un malinteso comune. Microsft.VisualBasic contiene tutte le procedure comuni di Visual Basic che rendono la codifica in VB molto più semplice. Microsot.VisualBasic.Compatibility è l'assembly utilizzato per l'eredità VB6.
AMissico,

63
Esistono oltre 2.000 righe di codice in Microsoft.VisualBasic.Devices.Computer.FileSystem. CopyDirectory assicura che non si stia copiando una cartella principale in una cartella secondaria e altri controlli. È altamente ottimizzato e così via. La risposta selezionata è al massimo un codice fragile.
AMissico,

17
@AMissico - ok, allora perché questo codice ottimizzato e completo è incluso Microsoft.VisualBasice no System.IO? Il motivo per cui non è in Mono è perché tutte le librerie che sono considerate "core" lo sono System.[something]- tutte le altre no. Non ho problemi a fare riferimento a una DLL aggiuntiva, ma c'è una buona ragione per cui Microsoft non ha incluso questa funzionalità System.IO.
Keith,

Risposte:


553

Molto più 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);

25
È davvero un bel pezzo di codice, ma questo non è il tipo di codice che può essere utilizzato ovunque. Gli sviluppatori dovrebbero fare attenzione perché dirPath.Replace potrebbe causare conseguenze indesiderate. Solo un avvertimento per le persone a cui piace fare copia e incolla sulla rete. Il codice pubblicato da @jaysponsored è più sicuro perché non usa string.Riposiziona ma sono sicuro che ha anche i suoi casi angolari.
Alex

17
Prestare attenzione a questo codice poiché genererà un'eccezione se la directory di destinazione esiste già. Inoltre non sovrascriverà i file già esistenti. Basta aggiungere un segno di spunta prima di creare ciascuna directory e utilizzare il sovraccarico di File.Copy per sovrascrivere il file di destinazione, se presente.
joerage

30
@Xaisoft - Replaceha un problema se hai un modello ripetitivo all'interno del percorso, per esempio "sourceDir/things/sourceDir/things"dovrebbe diventare "destinationDir/things/sourceDir/things", ma se usi sostituirlo diventa"destinationDir/things/destinationDir/things"
Keith

35
Perché *.*invece di *? Non vuoi copiare anche file senza estensioni?
Daryl,

10
Costruiamo qualcosa e contribuiamo a Open Source .NET Core ...: /
Mzn

231

Hmm, penso di aver frainteso la domanda, ma ho intenzione di rischiare. Cosa c'è di sbagliato nel seguente metodo semplice?

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 Dal momento che questo post ha raccolto un numero impressionante di voti negativi per una risposta così semplice a una domanda altrettanto semplice, vorrei aggiungere una spiegazione. Si prega di leggere questo prima di effettuare il downgrade .

Prima di tutto, questo codice non è inteso come sostituzione drop-in al codice nella domanda. È solo a scopo illustrativo.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectoryesegue alcuni ulteriori test di correttezza (ad es. se l'origine e il target sono directory valide, se l'origine è un genitore del target ecc.) che mancano da questa risposta. Quel codice è probabilmente anche più ottimizzato.

Detto questo, il codice funziona bene . È stato (quasi identico) usato per anni in un software maturo. A parte l'intrinseca instabilità presente con tutti i gestori IO (ad esempio cosa succede se l'utente scollega manualmente l'unità USB mentre il codice vi sta scrivendo?), Non ci sono problemi noti.

In particolare, vorrei sottolineare che l'uso della ricorsione qui non è assolutamente un problema. Né in teoria (concettualmente, è la soluzione più elegante) né in pratica: questo codice non supererà lo stack . Lo stack è abbastanza grande da gestire anche gerarchie di file profondamente nidificate. Molto prima che lo spazio dello stack diventi un problema, entra in gioco la limitazione della lunghezza del percorso della cartella.

Si noti che un utente malintenzionato potrebbe essere in grado di rompere questo presupposto utilizzando directory profondamente annidate di una lettera ciascuna. Non ci ho provato. Ma solo per illustrare il punto: al fine di far traboccare questo codice su un tipico computer, le directory dovrebbero essere nidificate alcune migliaia di volte. Questo non è semplicemente uno scenario realistico.


5
Questa è la ricorsione della testa. Può cadere in preda a un overflow dello stack se le directory sono nidificate abbastanza in profondità.
spoulson,

19
Fino a poco tempo fa, la profondità di annidamento delle directory era limitata dal sistema operativo. Dubito che troverai directory che sono nidificate più di qualche centinaio di volte (se anche). Il codice sopra può richiedere molto di più.
Konrad Rudolph,

5
Mi piace l'approccio ricorsivo, il rischio di overflow dello stack è minimo nel peggiore dei casi.
David Basarab,

49
@DTashkinov: beh, mi scusi ma sembra un po 'eccessivo. Perché il codice evidente == downvote? Il contrario dovrebbe essere vero. Il metodo integrato era già stato pubblicato, ma Keith chiese specificamente un altro metodo. Inoltre, cosa intendi con la tua ultima frase? Mi dispiace, ma non capisco affatto i motivi del tuo voto negativo.
Konrad Rudolph,

6
@AMissico: meglio di cosa ? Nessuno ha affermato che sia migliore del codice VB dal framework. Noi sappiamo che non è.
Konrad Rudolph,

132

Copiato da 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
Non c'è motivo di verificare se la directory esiste, basta chiamare Directoty.CreateDirectory che non farà nulla se la directory esiste già.
Tal Jerome,

1
Per coloro che desiderano affrontare percorsi superiori a 256 caratteri, è possibile utilizzare un pacchetto Nuget chiamato ZetaLongPaths
AK,

2
Questa risposta sembra essere la più utile di tutte. Utilizzando DirectoryInfo anziché le stringhe si evitano molti potenziali problemi.
Dedalo

50

Prova questo:

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

I tuoi argomenti di xcopy possono variare ma hai l'idea.


3
/ E gli dice di copiare tutte le sottodirectory (anche quelle vuote). / Dico che se la destinazione non esiste crea una directory con quel nome.
d4nt

6
aggiungi una doppia citazione per essere sicuro.
jaysonragasa,

6
Aggiungi / Y per evitare che venga richiesto di sovrascrivere i file esistenti. stackoverflow.com/q/191209/138938
Jon Crowell,

16
Scusa, ma è orribile. Presuppone che il sistema di destinazione sia Windows. Presuppone che le versioni future includano xcopy.exe in quel percorso specifico. Presuppone che i parametri di xcopy non cambino. Richiede di assemblare i parametri per xcopy come stringa, il che introduce un sacco di potenziale errore. Inoltre, l'esempio non menziona alcun errore nella gestione dei risultati del processo avviato, cosa che mi aspetterei, perché contrariamente ad altri metodi questo fallirebbe silenziosamente.
cel sharp

3
@MatthiasJansen, penso che tu l'abbia preso molto personale. La risposta è al punto e spiega molto su come raggiungerlo ... Dal momento che la domanda non richiede la compatibilità multipiattaforma o il non utilizzo di xcopy o qualsiasi altra cosa il poster ha appena risposto per spiegare come questo può essere ottenuto in un modo ... potrebbe essere 1000 modi per fare la stessa cosa e le risposte variano .. ecco perché questo forum è qui per rivolgersi e i programmatori di tutto il mondo vengono qui per condividere le loro esperienze. Giù voto il tuo commento.
KMX,

48

In alternativa, se si desidera procedere nel modo più duro, aggiungere un riferimento al progetto per Microsoft.VisualBasic e quindi utilizzare quanto segue:

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

Tuttavia, l'utilizzo di una delle funzioni ricorsive è un modo migliore di procedere poiché non dovrà caricare la DLL VB.


1
Questo non è molto diverso da come l'ho fatto, comunque, per poterlo fare devi comunque caricare le cose di compatibilità con le versioni precedenti di VB.
Keith,

10
Il caricamento del gruppo VB è costoso? Le opzioni VB sono molto più eleganti delle versioni C #.
jwmiller5,

3
Quale "materiale di compatibilità con le versioni precedenti di VB"? CopyDirectory utilizza Shell o Framework.
AMissico,

3
Vorrei che fosse acceso System.IO.Directory , ma è meglio che riscriverlo!
Josh M.,

2
Questo è il modo di andare imo, molto più facile di qualsiasi altra opzione
reggaeguitar,

38

Questo sito mi ha sempre aiutato molto e ora tocca a me aiutare gli altri con quello che so.

Spero che il mio codice qui sotto sia utile per qualcuno.

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
Ricorda la barra rovesciata finale
Alexey F,

24
Gente, usate Path.Combine(). Non usare mai la concatenazione di stringhe per unire i percorsi dei file.
Andy,

3
Hai un OBOB nello snippet di codice sopra riportato. Si dovrebbe usare source_dir.Length + 1, non è source_dir.Length.
PellucidWombat,

Questo codice è un buon concetto, ma ... Un file non deve avere un "." in esso, quindi sarebbe meglio usare ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories))
Jean Libera,

Grazie @JeanLibera, hai ragione. Ho cambiato il codice con il tuo suggerimento.
jaysponsored

14

Copia la cartella in modo ricorsivo senza ricorsione per evitare l'overflow dello stack.

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

utile modello di non ricorsione :)
Minh Nguyen,

2
Difficile immaginare di far esplodere la pila prima di illuminare il limite del percorso
Ed S.

5

Ecco una classe di utilità che ho usato per attività di IO come questa.

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

Si noti che Microsoft utilizza SHFileOperation internamente per Microsoft.VisualBasic.
jrh

3

Potrebbe non essere sensibile alle prestazioni, ma lo sto usando per cartelle da 30 MB e funziona perfettamente. Inoltre, non mi è piaciuta tutta la quantità di codice e ricorsione necessaria per un compito così semplice.

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

Nota: ZipFile è disponibile su .NET 4.5+ nello spazio dei nomi System.IO.Compression


1
Nemmeno io, da qui la domanda, ma la risposta selezionata non necessita di ricorsione. Questa risposta crea un file zip su disco, che richiede molto lavoro aggiuntivo per la copia di un file - non solo stai creando una copia aggiuntiva dei dati, ma stai spendendo il tempo del processore per comprimerlo e decomprimerlo. Sono sicuro che funziona, allo stesso modo in cui puoi probabilmente infilarti un chiodo con la scarpa, ma è più lavoro con più cose che possono andare storte, mentre ci sono modi migliori per farlo.
Keith il

Il motivo per cui ho finito con questo è la sostituzione di stringhe. Come altri hanno sottolineato, la risposta accettata presenta molte preoccupazioni; il collegamento di giunzione potrebbe non funzionare, così come ripetere la cartella o i file senza estensione o nome. Meno codice, meno possibilità di sbagliare. E poiché il tempo del processore non è un problema per me, lo rende adatto al mio caso specifico
AlexanderD

2
Sì, è come guidare a 1000 miglia di distanza per evitare un singolo semaforo, ma è il tuo viaggio, quindi fallo. Controllare i modelli di cartelle è banale rispetto a ciò che ZIP deve fare sotto il cofano. Lo sconsiglio vivamente a chiunque si preoccupi di non sprecare processore, disco, elettricità o dove questo deve essere eseguito insieme ad altri programmi sulla stessa macchina. Inoltre, se ti viene mai chiesto questo tipo di domande durante l'intervista, non andare mai con "il mio codice è semplice, quindi non mi interessa il tempo del processore" - non otterrai il lavoro.
Keith,

1
Sono passato alla risposta fornita da @ justin-r . Tuttavia, lascerò questa risposta lì solo come un altro modo di farlo
AlexanderD

1
Se le cartelle si trovano su condivisioni di rete separate e contengono molti file, questa sarebbe l'opzione migliore secondo me.
Danny Parker,

2

Un piccolo miglioramento sulla risposta di d4nt, poiché probabilmente si desidera verificare la presenza di errori e non è necessario modificare i percorsi xcopy se si lavora su un server e una macchina di sviluppo:

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

Questo è il mio codice spero che questo aiuto

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

Vedi la risposta selezionata, usando il SearchOptionflag nelle ricerche di cartelle e file lo fa in 4 righe di codice. Controlla anche l' .HasFlagestensione ora su enums.
Keith,

2

Se ti piace la risposta popolare di Konrad, ma vuoi che lo sourcestesso sia una cartella sotto target, anziché mettere i suoi figli sotto la targetcartella, ecco il codice per quello. Restituisce il nuovo creato DirectoryInfo, il che è utile:

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

È sempre possibile utilizzare questo , tratto dal sito 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);
        }
    }
}

1
Questo è fantastico - Tieni presente che la riga file.CopyTo(temppath, false);dice "copia questo file in questo posto, solo se non esiste", che il più delle volte non è quello che vogliamo. Ma capisco perché è l'impostazione predefinita. Forse aggiungi un flag al metodo per sovrascrivere i file.
Andy,

2

tboswell sostituisce la versione di prova (che è resistente al motivo ripetuto nel percorso del file)

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
Gente, usate Path.Combine(). Non usare mai la concatenazione di stringhe per unire i percorsi dei file.
Andy,

2

La mia soluzione è sostanzialmente una modifica della risposta di @ Termininja, tuttavia l'ho migliorata un po 'e sembra essere 5 volte più veloce della risposta accettata.

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 modifica di @Ahmed Sabry in foreach parallela completa produce un risultato migliore, tuttavia il codice utilizza la funzione ricorsiva e non è l'ideale in alcune situazioni.

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

Ci scusiamo per il codice precedente, aveva ancora dei bug :( (cadde in preda al problema della pistola più veloce). Qui è testato e funzionante. La chiave è SearchOption.AllDirectories, che elimina la necessità di ricorsioni esplicite.

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

Ecco un metodo di estensione per DirectoryInfo a la FileInfo.CopyTo (notare il overwriteparametro):

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

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

1
Questo è simile ad altre risposte, refactored per l'uso .ToList().ForEach((che è leggermente più lavoro, memoria e leggermente più lento rispetto al semplice enumerazione diretta delle directory) e come metodo di estensione. La risposta selezionata usa SearchOption.AllDirectoriesed evita la ricorsione, quindi consiglierei di passare a quel modello. Inoltre, di solito non è necessario il nome del tipo nei metodi di estensione - lo rinominerei in CopyTo()modo che diventassesourceDir.CopyTo(destination);
Keith

1

Una variante con un solo loop per la copia di tutte le cartelle e file:

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

Se hai intenzione di usarlo Regex, probabilmente dovresti anche far Regex.Escape(path)parte della composizione dell'espressione (specialmente considerando il separatore del percorso di Windows). Si potrebbe anche ottenere benefici dalla creazione (e forse la compilazione) un new Regex()al di fuori oggetto del ciclo, piuttosto che basarsi sul metodo statico.
jimbobmcgee,

0

Meglio di qualsiasi codice (metodo di estensione a DirectoryInfo con ricorsione)

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
Non sono sicuro di ciò che si aggiunge alla risposta accettata, a parte l'utilizzo della ricorsione (dove non è necessario) e nascondere le eccezioni per rendere più difficile il debug.
Keith,

0

Copia e sostituisci tutti i file della cartella

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

Saluti per la risposta, ma non sono sicuro di ciò che aggiunge. Inoltre try catch throwè inutile.
Keith

0

Il codice seguente è suggerimento di Microsoft come copiare le directory ed è condiviso dal caro @iato ma copia semplicemente le sottodirectory e i file della cartella di origine in modo ricorsivo e non copia la cartella di origine da sola (come fare clic destro -> copia ).

ma c'è un modo complicato sotto questa risposta:

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

se vuoi copiare ricorsivamente il contenuto della cartella sorgente e delle sottocartelle puoi semplicemente usarlo in questo modo:

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

ma se vuoi copiare la directory di origine da sola (simile a quando hai fatto clic con il tasto destro sulla cartella di origine e hai fatto clic su Copia, quindi nella cartella di destinazione hai fatto clic su Incolla) dovresti usare in questo modo:

 string source = @"J:\source\";
 string dest= @"J:\destination\";
 DirectoryCopy(source, Path.Combine(dest, new DirectoryInfo(source).Name));

sono già state pubblicate alcune risposte di seguito: stackoverflow.com/a/45199038/1951524
Martin Schneider,

Grazie @ MA-Maddin, ma copia la cartella sorgente stessa? o solo i contenuti?
Arash.Zandi,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.