2008-09-12 36 views
397

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

No parece haber una forma de hacerlo con las clases System.IO sin mucha recursión.

hay un método en VB que podemos utilizar si añadimos una referencia a Microsoft.VisualBasic:

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

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

+78

diría que mirando las alternativas publican a continuación, que la forma de VB no se ve tan feo. –

+1

La verdadera pregunta es, ¿por qué no está esto en la biblioteca IO predeterminada? Por ahora, probablemente todos hayamos puesto el mismo código en nuestra biblioteca personal. –

+30

¿Cómo puede ser un truco cuando es parte de .NET Framework? Deja de escribir código y usa lo que tienes. – AMissico

Respuesta

422

Mucho 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); 
+0

Idea genial: no sé por qué nunca pensé en usar 'SearchOption.AllDirectories'. Probablemente usaría el método 'SubString' en lugar de' Replace', pero eso es solo cosas de estilo de codificación. – Keith

+7

+1 Gracias, código muy lacónico para esa tarea. – Genius

+14

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 por la red. El código publicado por @jaysponsored es más seguro porque no usa cadenas. Reemplazar, pero estoy seguro de que también tiene sus casos de esquina. – Alex

31

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

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

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

+1

Eso no es muy diferente de cómo lo hice de todos modos, aún necesita cargar las características de compatibilidad con versiones anteriores de VB para poder hacerlo. – Keith

+9

¿Es caro cargar el conjunto de VB? Las opciones de VB son mucho más elegantes que las versiones de C#. – jwmiller5

+3

¿Qué "cosas de compatibilidad con versiones anteriores de VB"? CopyDirectory utiliza el Shell o el Framework. – AMissico

1

Disculpe por el código anterior, todavía tenía errores :((presa del problema de arma más rápida). Aquí está probado y funcionando. La clave es SearchOption.AllDirectories, que elimina la necesidad de recursión 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); 
    } 
} 
5

Aquí es una clase de utilidad que he usado para tareas de IO como este.

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

Hmm, creo que entienden mal la pregunta, pero voy a correr el riesgo. ¿Qué hay de malo en el siguiente sencillo método?

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 Desde esta publicación se ha ganado un número impresionante de downvotes para una respuesta tan simple de un igualmente simple pregunta, permítanme añadir una explicación. Por favor,lea esto antes de la votación negativa.

En primer lugar, Este código no está intendend como un reemplazo directo al código en la pregunta. Es solo para fines ilustrativos.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectory realiza algunas pruebas de corrección adicionales (por ejemplo, si el origen y el destino son directorios válidos, si el origen es un elemento primario 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. Es tiene (casi idénticamente) se ha utilizado en un software maduro durante años. Además de la inconstancia inherente presente con todas las manipulaciones IO (por ejemplo, ¿qué pasa si el usuario desenchufa manualmente la unidad USB mientras el código está escribiendo en ella?), No hay problemas conocidos.

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

Observe que un usuario malintencionado podría ser capaz de romper esta suposición mediante el uso de directorios profundamente anidados de una letra cada uno. No he intentado esto. Pero solo para ilustrar el punto: para hacer que este código se desborde en una computadora típica, los directorios tendrían que estar anidados unos pocos mil veces. Esto simplemente no es un escenario realista.

+0

Nowt, 'excepto la llamada recursiva. ¿Por qué tenemos que hacer eso en C#? – Keith

+13

¿Qué pasa con la recursión? –

+4

Esta es la recursividad principal. Puede ser víctima de un desbordamiento de pila si los directorios están anidados lo suficientemente profundo. – spoulson

45

Prueba esto:

Process proc = new Process(); 
proc.StartInfo.UseShellExecute = true; 
proc.StartInfo.FileName = @"C:\WINDOWS\system32\xcopy.exe"; 
proc.StartInfo.Arguments = @"C:\source C:\destination /E /I"; 
proc.Start(); 

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

+0

¿qué significa el/E/I? ¿Exagerar? – aron

+1

/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

+6

agrega una comilla doble para estar seguro. – jaysonragasa

89

Copiado del 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. 
} 
+4

Diría que esta es la solución más elegante. –

+5

No hay ninguna razón para verificar si el directorio existe, simplemente llame a Directoty.CreateDirectory que no hará nada si el directorio ya existe. –

+0

Gracias Tal, actualizado. –

12

carpeta de copia recursiva sin recursividad para evitar el desbordamiento de 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; 
    } 
} 
21

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

Espero que mi código a continuación 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))); 
     // 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))); 
    } 
+0

Recuerde acerca de la barra invertida final –

+6

Gente, use 'Path.Combine()'. Nunca use la concatenación de cadenas para unir las rutas de archivos. – Andy

+0

Tiene un OBOB en el fragmento de código anterior. Deberías usar 'source_dir.Length + 1', no' source_dir.Length'. – PellucidWombat

1

Una pequeña mejora en la respuesta de d4nt, ya que es probable que desee comprobar si hay errores y no tener que cambiar las rutas de xcopy si está trabajando en un servidor y desarrollo de la máquina:

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

Este es mi código esperanza 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; 
    } 
+0

Vea la respuesta seleccionada, al usar el indicador 'SearchOption' en las búsquedas de carpetas y archivos lo hace en 4 líneas de código. También consulte la extensión '.HasFlag' ahora en enums. – Keith

1

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

public static DirectoryInfo CopyTo(this DirectoryInfo sourceDir, string destinationPath, bool overwrite = false) 
{ 
    var sourcePath = sourceDir.FullName; 

    var destination = new DirectoryInfo(destinationPath); 

    destination.Create(); 

    foreach (var sourceSubDirPath in Directory.EnumerateDirectories(sourcePath, "*", SearchOption.AllDirectories)) 
     Directory.CreateDirectory(sourceSubDirPath.Replace(sourcePath, destinationPath)); 

    foreach (var file in Directory.EnumerateFiles(sourcePath, "*", SearchOption.AllDirectories)) 
     File.Copy(file, file.Replace(sourcePath, destinationPath), overwrite); 

    return destination; 
} 
2

Si te gusta la respuesta popular de Konrad, pero desea que el source a sí misma como una carpeta bajo target, en lugar de ponerlo de los niños en la carpeta target, aquí está el código para eso. Devuelve el recién creado DirectoryInfo, lo cual es práctico:

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

tboswell 's reemplazar a la versión prueba (que es resistente al patrón de ruta de archivo repetir)

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

Gente, use 'Path.Combine()'. Nunca use la concatenación de cadenas para unir las rutas de archivos. – Andy

1

siempre se puede utilizar this, tomado de Microsoft de sitio web.

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

Esto es genial: tenga en cuenta el 'archivo de línea'. Copiar a (ruta de acceso temporal, falso); 'dice" copiar este archivo a este lugar, solo si no existe ", que la mayoría de las veces no es lo que querer. Pero, puedo entender por qué no funciona eso. Tal vez agregue una marca al método para sobrescribir archivos. – Andy

0

Utilice 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); 
    } 
} 
+1

Esto es similar a otras respuestas, refactorizado para usar '.ToList(). ForEach (' (que es un poco más de 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 utiliza 'SearchOption .AllDirectories' y evita la recursión, así que recomendaría cambiar a ese modelo. Además, por lo general, no necesita el nombre del tipo en los métodos de extensión. Le cambiaría el nombre a 'CopyTo()' para que se convirtiera en ' sourceDir.CopyTo (destination); ' – Keith

+0

Gracias, respuesta editada. –

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

No estoy seguro de lo que esto agrega a la respuesta aceptada, aparte de usar recursión (donde no es necesario) y ocultar excepciones para dificultar la depuración. – Keith

Cuestiones relacionadas