Copie todo el contenido de un directorio en C #

524

Quiero copiar todo el contenido de un directorio de una ubicación a otra en C #.

No parece haber una manera de hacer esto usando System.IOclases sin mucha recursividad.

Hay un método en VB que podemos usar si agregamos una referencia a Microsoft.VisualBasic:

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

Esto parece un truco bastante feo. ¿Hay una mejor manera?

Keith
fuente
101
Diría que mirando las alternativas publicadas a continuación, que la forma VB no se ve tan fea.
Kevin Kershaw
41
¿Cómo puede ser un hack cuando es parte de .NET Framework? Deja de escribir código y usa lo que tienes.
AMissico
15
Ese es un error común. Microsft.VisualBasic contiene todos los procedimientos comunes de Visual Basic que hacen que la codificación en VB sea mucho más fácil. Microsot.VisualBasic.Compatibility es el ensamblaje utilizado para el legado VB6.
AMissico
63
Hay más de 2,000 líneas de código para Microsoft.VisualBasic.Devices.Computer.FileSystem. CopyDirectory garantiza que no esté copiando una carpeta principal en una carpeta secundaria y otras comprobaciones. Está altamente optimizado, y así sucesivamente. La respuesta seleccionada es el código frágil en el mejor de los casos.
AMissico
17
@ AMissico - ok, entonces ¿por qué está este código optimizado y completo Microsoft.VisualBasicy no System.IO? La razón por la que no está en Mono es porque todas las bibliotecas que se consideran 'núcleo' son System.[something], todas las demás no lo son. No tengo ningún problema para hacer referencia a una DLL adicional, pero hay una buena razón por la cual Microsoft no ha incluido esta función System.IO.
Keith el

Respuestas:

553

Más fácil

//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
fuente
25
Es una buena pieza de código, pero este no es el tipo de código que se puede usar en cualquier lugar. Los desarrolladores deben tener cuidado porque dirPath.Replace podría causar consecuencias no deseadas. Solo una advertencia para las personas que les gusta copiar y pegar en la red. El código publicado por @jayspon patrocinado es más seguro porque no usa cadenas. Reemplace, pero estoy seguro de que también tiene sus esquinas.
Alex
17
Tenga cuidado con este código, ya que arrojará una excepción si el directorio de destino ya existe. Tampoco sobrescribirá los archivos que ya existen. Simplemente agregue una marca de verificación antes de crear cada directorio y use la sobrecarga de File.Copy para sobrescribir el archivo de destino si existe.
joerage
30
@Xaisoft - Replacetiene un problema si usted tiene un patrón que se repite dentro de la ruta, por ejemplo, "sourceDir/things/sourceDir/things"debería ser "destinationDir/things/sourceDir/things", pero si se utiliza reemplazarlo se convierte en"destinationDir/things/destinationDir/things"
Keith
35
¿Por qué en *.*lugar de *? ¿No quieres copiar archivos sin extensiones también?
Daryl
10
Construyamos algo y contribuyamos al Open Source .NET Core ...: /
Mzn
231

Hmm, creo que no he entendido bien la pregunta, pero voy a arriesgarme. ¿Qué tiene de malo el siguiente método sencillo?

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

EDITAR Dado que esta publicación ha cosechado una cantidad impresionante de votos negativos para una respuesta tan simple a una pregunta igualmente simple, permítanme agregar una explicación. Por favor lea esto antes de votar abajo .

En primer lugar, este código no se incluye como un reemplazo directo del código en la pregunta. Es solo para fines ilustrativos.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectoryrealiza algunas pruebas de corrección adicionales (por ejemplo, si el origen y el destino son directorios válidos, si el origen es un padre del destino, etc.) que faltan en esta respuesta. Ese código probablemente también esté más optimizado.

Dicho esto, el código funciona bien . Se ha utilizado (casi idénticamente) en un software maduro durante años. Además de la inconstancia inherente presente con todos los manejos de E / S (por ejemplo, ¿qué sucede si el usuario desconecta manualmente la unidad USB mientras su código está escribiendo en ella?), No hay problemas conocidos.

En particular, me gustaría señalar que el uso de la recursividad aquí no es absolutamente un problema. Ni en teoría (conceptualmente, es la solución más elegante) ni en la práctica: este código no desbordará la pila . La pila es lo suficientemente grande como para manejar incluso jerarquías de archivos profundamente anidadas. Mucho antes de que el espacio de pila se convierta en un problema, la limitación de longitud de la ruta de la carpeta entra en juego.

Tenga en cuenta que un usuario malintencionado podría romper esta suposición utilizando directorios profundamente anidados de una letra cada uno. No he intentado esto. Pero solo para ilustrar el punto: para que este código se desborde en una computadora típica, los directorios tendrían que anidarse unos miles de veces. Esto simplemente no es un escenario realista.

Konrad Rudolph
fuente
55
Esta es la recursión de la cabeza. Puede caer presa de un desbordamiento de pila si los directorios están anidados lo suficientemente profundo.
spoulson
19
Hasta hace muy poco, la profundidad de anidación del directorio estaba restringida por el sistema operativo. Dudo que encuentre directorios anidados más de unos cientos de veces (si es que es así). El código anterior puede tomar mucho más.
Konrad Rudolph el
55
Me gusta el enfoque recursivo, el riesgo de un desbordamiento de pila es mínimo en el peor de los casos.
David Basarab
49
@DTashkinov: perdón, pero eso parece un poco excesivo. ¿Por qué es obvio el código == downvote? Lo contrario debería ser cierto. El método incorporado ya había sido publicado, pero Keith solicitó específicamente otro método. Además, ¿qué quieres decir con tu última oración? Lo siento, pero no entiendo tus razones para votar negativamente.
Konrad Rudolph el
66
@ AMissico: ¿mejor que qué ? Nadie afirmó que fuera mejor que el código VB del marco. Nosotros sabemos que no lo es.
Konrad Rudolph
132

Copiado 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.
fuente
8
No hay razón para verificar si el directorio existe, simplemente llame a Directoty.CreateDirectory que no hará nada si el directorio ya existe.
Tal Jerome
1
Para aquellos que buscan lidiar con rutas de más de 256 caracteres, puede usar un paquete Nuget llamado ZetaLongPaths
AK
2
Esta respuesta parece ser la más útil de todas. Al usar DirectoryInfo en lugar de cadenas, se evitan muchos problemas potenciales.
DaedalusAlpha
50

Prueba esto:

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

Sus argumentos de xcopy pueden variar, pero se entiende la idea.

d4nt
fuente
3
/ E le dice que copie todos los subdirectorios (incluso los vacíos). / Le digo que si el destino no existe, cree un directorio con ese nombre.
d4nt
66
agregue comillas dobles para estar seguro.
jaysonragasa
66
Agregue / Y para evitar que se le solicite sobrescribir los archivos existentes. stackoverflow.com/q/191209/138938
Jon Crowell
16
Lo siento, pero esto es horrible. Se supone que el sistema de destino es Windows. Se supone que las versiones futuras incluyen xcopy.exe en esa ruta específica. Se supone que los parámetros de xcopy no cambian. Requiere ensamblar los parámetros para xcopy como una cadena, lo que introduce un gran potencial de error. Además, la muestra no menciona ningún manejo de errores para los resultados del proceso iniciado, lo que esperaría, porque a diferencia de otros métodos, esto fallaría en silencio.
cel sharp
3
@MatthiasJansen, creo que lo tomaste muy personal. La respuesta es directa y explica mucho acerca de cómo lograrlo ... Dado que la pregunta no exige la compatibilidad multiplataforma o no usar xcopy o cualquier otra cosa, el póster solo respondió para explicar cómo se puede lograr esto de una manera ... Puede haber 1000 formas de hacer lo mismo y las respuestas varían ... es por eso que este foro está aquí para dirigirse y los programadores de todo el mundo vienen aquí para compartir sus experiencias. Voto abajo tu comentario.
KMX
48

O, si desea ir por el camino difícil, agregue una referencia a su proyecto para Microsoft.VisualBasic y luego use lo siguiente:

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

Sin embargo, usar una de las funciones recursivas es una mejor manera de hacerlo, ya que no tendrá que cargar el dll de VB.

Josef
fuente
1
De todos modos, eso no es realmente diferente de cómo lo hice: aún necesita cargar las cosas de compatibilidad con versiones anteriores de VB para poder hacerlo.
Keith
10
¿Cargar el ensamblaje VB es costoso? Las opciones de VB son mucho más elegantes que las versiones de C #.
jwmiller5
3
¿Qué "material de compatibilidad con versiones anteriores de VB"? CopyDirectory utiliza el Shell o el Framework.
AMissico
3
Desearía que estuviera encendido System.IO.Directory, ¡pero es mejor que reescribirlo!
Josh M.
2
Este es el camino a seguir, mucho más fácil que cualquiera de las otras opciones
reggaeguitar
38

Este sitio siempre me ha ayudado mucho y ahora es mi turno de ayudar a los demás con lo que sé.

Espero que mi siguiente código sea útil para alguien.

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)));
    }
Jay patrocinado
fuente
1
Recuerde sobre la barra invertida final
Alexey F
24
Amigos, uso Path.Combine(). Nunca use la concatenación de cadenas para juntar rutas de archivos.
Andy
3
Tiene un OBOB en el fragmento de código anterior. Deberías estar usando source_dir.Length + 1, no source_dir.Length.
PellucidWombat
Este código es un buen concepto, pero ... Un archivo no tiene que tener un "." en él, por lo que sería mejor usar ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories)
Jean Libera
Gracias @JeanLibera, tienes razón. Cambié el código con tu sugerencia.
patrocinado el
14

Copie la carpeta de forma recursiva sin recurrencia para evitar el desbordamiento de la pila.

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
fuente
plantilla útil no recurrente :)
Minh Nguyen
2
Es difícil imaginar que sopla la pila antes brillando el límite ruta
Ed S.
5

Aquí hay una clase de utilidad que he usado para tareas de IO como esta.

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

fuente
Tenga en cuenta que Microsoft usa SHFileOperation internamente para Microsoft.VisualBasic.
jrh
3

Puede que no tenga en cuenta el rendimiento, pero lo estoy usando para carpetas de 30 MB y funciona perfectamente. Además, no me gustó toda la cantidad de código y recursión requerida para una tarea tan fácil.

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 está disponible en .NET 4.5+ en el espacio de nombres System.IO.Compression

AlexanderD
fuente
1
Yo tampoco, de ahí la pregunta, pero la respuesta seleccionada no necesita recurrencia. Esta respuesta crea un archivo zip en el disco, que es mucho trabajo adicional para la copia de un archivo; no solo está creando una copia adicional de los datos, sino que está gastando el tiempo del procesador comprimiéndolo y descomprimiéndolo. Estoy seguro de que funciona, de la misma manera que probablemente puedas clavar un clavo con tu zapato, pero es más trabajo con más cosas que pueden salir mal, mientras que hay mejores formas de hacerlo.
Keith
La razón por la que terminé con esto es el reemplazo de cadenas. Como otros han señalado, la respuesta aceptada presenta muchas preocupaciones; el enlace de unión puede no funcionar, así como repetir patrones de carpeta o archivos sin extensión o nombre. Menos código, menos posibilidades de equivocarse. Y dado que el tiempo de procesador no es una preocupación para mí, lo hace adecuado para mi caso específico
AlexanderD
2
Sí, eso es como conducir 1000 millas fuera de tu camino para evitar un solo semáforo, pero es tu viaje, así que anímate. La comprobación de patrones de carpeta es trivial en comparación con lo que ZIP debe hacer debajo del capó. Recomiendo encarecidamente contra esto a cualquiera que se preocupe por no desperdiciar el procesador, el disco, la electricidad o donde esto deba ejecutarse junto con otros programas en la misma máquina. Además, si alguna vez le hacen este tipo de preguntas en la entrevista, nunca vaya con "mi código es simple, así que no me importa el tiempo del procesador", no obtendrá el trabajo.
Keith
1
Cambié a la respuesta proporcionada por @ justin-r . Aún así, dejaré esta respuesta allí como otra forma de hacerlo
AlexanderD
1
Si las carpetas están en recursos compartidos de red separados y contienen muchos archivos, esta sería la mejor opción en mi opinión.
Danny Parker
2

Una mejora menor en la respuesta de d4nt, ya que probablemente desee verificar los errores y no tener que cambiar las rutas de xcopy si está trabajando en un servidor y una máquina de desarrollo:

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

Este es mi código espero esta ayuda

    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
fuente
Vea la respuesta seleccionada, usando la SearchOptionbandera en las búsquedas de carpetas y archivos, lo hace en 4 líneas de código. Consulte también la .HasFlagextensión ahora en enumeraciones.
Keith
2

Si te gusta la respuesta popular de Konrad, pero quieres sourceque sea una carpeta debajo target, en lugar de poner a sus hijos debajo de la targetcarpeta, aquí está el código para eso. Devuelve el recién creado DirectoryInfo, que es útil:

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

Siempre puede usar esto , tomado del sitio web de 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
fuente
1
Esto es genial: tenga en cuenta que la línea file.CopyTo(temppath, false);dice "copie este archivo a este lugar, solo si no existe", que la mayoría de las veces no es lo que queremos. Pero, puedo entender por qué lo predeterminado es eso. Tal vez agregue una bandera al método para sobrescribir archivos.
Andy
2

Versión de prueba de reemplazo de tboswell (que es resistente a la repetición de patrones en filepath)

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
fuente
3
Amigos, uso Path.Combine(). Nunca use la concatenación de cadenas para juntar rutas de archivos.
Andy
2

Mi solución es básicamente una modificación de la respuesta de @ Termininja, sin embargo, la he mejorado un poco y parece ser más de 5 veces más rápida que la respuesta aceptada.

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

EDITAR: Modificar @Ahmed Sabry a foreach paralelo completo produce un mejor resultado, sin embargo, el código usa la función recursiva y no es ideal en alguna situación.

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

Perdón por el código anterior, todavía tenía errores :( (cayó presa del problema de arma más rápido). Aquí está probado y funciona. La clave es SearchOption.AllDirectories, que elimina la necesidad de recurrencia explícita.

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

Aquí hay un método de extensión para DirectoryInfo a la FileInfo.CopyTo (tenga en cuenta el overwriteparámetro):

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

Usa esta clase.

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
fuente
1
Esto es similar a otras respuestas, refactorizadas para su uso .ToList().ForEach((que es un poco más trabajo, memoria y un poco más lento que simplemente enumerar los directorios directamente) y como un método de extensión. La respuesta seleccionada usa SearchOption.AllDirectoriesy evita la recurrencia, por lo que recomendaría cambiar a ese modelo. Además, por lo general, no es necesario el nombre del tipo en los métodos de extensión - Me cambiarle el nombre a CopyTo()fin de que se hizosourceDir.CopyTo(destination);
Keith
1

Una variante con un solo bucle para copiar todas las carpetas y archivos:

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
fuente
Si va a usar Regex, probablemente también debería Regex.Escape(path)formar parte de su composición de expresión (especialmente teniendo en cuenta el separador de ruta de Windows). También puede obtener beneficios al crear (y tal vez compilar) un new Regex()objeto fuera del ciclo, en lugar de depender del método estático.
jimbobmcgee
0

Mejor que cualquier código (método de extensión a DirectoryInfo con recursividad)

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
fuente
1
No estoy seguro de lo que esto agrega sobre la respuesta aceptada, aparte de usar la recursión (donde no es necesario) y ocultar excepciones para dificultar la depuración.
Keith
0

Copie y reemplace todos los archivos de la carpeta

        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
fuente
Aplausos por la respuesta, pero no estoy seguro de lo que esto agrega. También el no try catch throwtiene sentido.
Keith
0

El siguiente código es una sugerencia de Microsoft sobre cómo copiar directorios y lo comparte querido @iato, pero solo copia subdirectorios y archivos de la carpeta de origen de forma recursiva y no copia la carpeta de origen. (como hacer clic con el botón derecho -> copiar )

pero hay una forma complicada debajo de esta respuesta:

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 desea copiar el contenido de la carpeta de origen y las subcarpetas de forma recursiva, simplemente puede usarlo así:

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

pero si desea copiar el directorio de origen por sí mismo (de forma similar a como hizo clic derecho en la carpeta de origen y luego hizo clic en copiar y luego en la carpeta de destino en la que hizo clic en pegar), debe usar así:

 string source = @"J:\source\";
 string dest= @"J:\destination\";
 DirectoryCopy(source, Path.Combine(dest, new DirectoryInfo(source).Name));
Arash.Zandi
fuente
Ya se han publicado algunas respuestas a continuación: stackoverflow.com/a/45199038/1951524
Martin Schneider
Gracias @ MA-Maddin, pero ¿copia la carpeta de origen en sí? o solo el contenido?
Arash.Zandi