CodeGym /Cursos /C# SELF /Copiado de archivos y directorios

Copiado de archivos y directorios

C# SELF
Nivel 40 , Lección 2
Disponible

1. Copiado de archivos

Hasta ahora nuestras operaciones sobre el sistema de archivos eran algo así como "un solo golpe": creaste el archivo, lo leíste, lo borraste — y ya. Pero en el mundo real a menudo hay que copiar contenido: backups de documentos, duplicar plantillas, automatizar procesos con datos. Copiar parece una tarea de niños (Ctrl+C y Ctrl+V — lo nuestro), pero en realidad hay muchos matices.

Hoy veremos todas las formas de copiar archivos y carpetas — desde las más simples hasta las un poco más complejas. También veremos cómo las clases integradas de .NET resuelven esto, qué limitaciones hay, qué errores pueden acechar y qué hacer si resulta que hay dos mil directorios y un millón de archivos.

La clase File y el método Copy

La forma más simple de copiar un archivo es usar el método estático File.Copy. Este método recibe la ruta del archivo fuente, la ruta del nuevo archivo y un parámetro opcional: permitir o no la sobreescritura si el archivo ya existe.

using System.IO;

// Ejemplo sencillo de copiado de archivo
File.Copy("source.txt", "destination.txt");

Si el archivo destino ya existe, el método lanzará una excepción. Para permitir explícitamente la sobreescritura, usa el tercer parámetro:

File.Copy("source.txt", "destination.txt", overwrite: true);

Punto importante: Si el segundo parámetro ("destination.txt") es la ruta a un directorio existente y no a un archivo, se producirá un error. ¡El método espera la ruta a un archivo!

Trabajo con rutas

Como siempre, no olvides usar Path.Combine para no caer en las trampas de barras dobles o invertidas:

string sourcePath = Path.Combine("Data", "input.txt");
string destPath = Path.Combine("Backup", "input_backup.txt");

File.Copy(sourcePath, destPath, overwrite: true);

Manejo de errores

¿Qué puede salir mal al copiar un archivo? Mucho: el archivo puede estar en uso por otro proceso, el archivo fuente puede faltar, puede que no tengas permisos, el disco destino puede estar lleno. Usa manejo de excepciones:

try
{
    File.Copy("bigdata.txt", "bigdata_backup.txt", overwrite: false);
    Console.WriteLine("¡Archivo copiado con éxito!");
}
catch (IOException ex)
{
    Console.WriteLine($"Error de E/S: {ex.Message}");
}
catch (UnauthorizedAccessException)
{
    Console.WriteLine("No hay acceso al archivo o carpeta.");
}
catch (Exception ex)
{
    Console.WriteLine($"Otro error: {ex.Message}");
}

La clase FileInfo y el método CopyTo

Si ya tienes un objeto FileInfo, puedes llamar en él el método CopyTo:

var fi = new FileInfo("report.xlsx");
fi.CopyTo("backup_report.xlsx");

El tercer argumento (overwrite) en CopyTo apareció solo en .NET Core 2.0+, así que si por alguna razón programas para una versión antigua del framework — no te extrañe un error.

Copiar un archivo "a ningún lado" (o "no te pillan — no robas")

Presta atención a la ruta de destino al copiar. Si la carpeta destino no existe, .NET lanzará un error. Por eso antes de copiar un archivo es buena idea asegurarse de que el directorio receptor existe:

string backupDir = "Backup";
if (!Directory.Exists(backupDir))
{
    Directory.CreateDirectory(backupDir);
}

string targetPath = Path.Combine(backupDir, "mydoc.txt");
File.Copy("mydoc.txt", targetPath);

2. Copiado de directorios: tarea para valientes

Aquí es cuando empiezan las verdaderas aventuras. En .NET no existe un método "mágico" Directory.Copy que lo haga todo en una línea (como con la clase File). Tocará currar un poco y escribir una función para copiar recursivamente todos los archivos y subcarpetas.

¿Por qué no existe Directory.Copy?

Copiar directorios no siempre es trivial. Hay que tener en cuenta que cada carpeta puede contener archivos, subcarpetas, archivos ocultos, archivos con permisos especiales y rutas largas. Por eso los desarrolladores de .NET decidieron: "Que los programadores elijan qué quieren copiar." Pero nosotros no buscamos atajos — ¡escribiremos nuestra función!

Ejemplo de copiado recursivo de directorio

Tarea: copiar el contenido de una carpeta (y todo su subuniverso) a otra.

using System;
using System.IO;

void CopyDirectory(string sourceDir, string destDir, bool recursive)
{
    // Comprobamos si la carpeta fuente existe
    if (!Directory.Exists(sourceDir))
        throw new DirectoryNotFoundException($"Carpeta fuente no encontrada: {sourceDir}");

    // Creamos la carpeta destino si aún no existe
    if (!Directory.Exists(destDir))
        Directory.CreateDirectory(destDir);

    // Copiamos todos los archivos
    foreach (string filePath in Directory.GetFiles(sourceDir))
    {
        string fileName = Path.GetFileName(filePath);
        string destFilePath = Path.Combine(destDir, fileName);
        File.Copy(filePath, destFilePath, overwrite: true);
    }

    // Si es recursivo — copiamos todas las subcarpetas
    if (recursive)
    {
        foreach (string dirPath in Directory.GetDirectories(sourceDir))
        {
            string dirName = Path.GetFileName(dirPath);
            string destSubDir = Path.Combine(destDir, dirName);
            // ¡Llamada recursiva!
            CopyDirectory(dirPath, destSubDir, recursive);
        }
    }
}

Uso:

CopyDirectory("C:\\MyData", "D:\\Backup\\MyData", recursive: true);

Esta función crea la estructura de directorios y copia todos los archivos, incluyendo el contenido de las subcarpetas.

Analizando el código y matices

Primero creamos el directorio destino (si no existe) — de lo contrario al intentar copiar un archivo ahí obtendremos un error. Dentro de cada bucle usamos Path.GetFileName para no perder el nombre del archivo o carpeta al construir la nueva ruta.

Por cierto, si copias un directorio en sí mismo o en una subcarpeta suya, recibirás una recursión épica… y un StackOverflowException. No copies "C:\\Data" en "C:\\Data\\Backup". ¡El ordenador se enfadará!

Copiar solo archivos (sin subcarpetas)

A veces basta con copiar solo los archivos del nivel superior (sin adentrarse en subcarpetas):

void CopyFilesOnly(string sourceDir, string destDir)
{
    if (!Directory.Exists(destDir))
        Directory.CreateDirectory(destDir);

    foreach (string filePath in Directory.GetFiles(sourceDir))
    {
        string fileName = Path.GetFileName(filePath);
        string destFilePath = Path.Combine(destDir, fileName);
        File.Copy(filePath, destFilePath, overwrite: true);
    }
}

Ejemplo — implementamos un backup

Añadamos esta funcionalidad a nuestra "Aplicación Doméstica" que vamos desarrollando a lo largo del curso. Que ahora sepa hacer una copia de seguridad de sus datos.

using System;
using System.IO;

namespace HomeApp
{
    class Program
    {
        static void CopyDirectory(string sourceDir, string destDir, bool recursive)
        {
            if (!Directory.Exists(sourceDir))
                throw new DirectoryNotFoundException($"Carpeta fuente no encontrada: {sourceDir}");

            if (!Directory.Exists(destDir))
                Directory.CreateDirectory(destDir);

            foreach (string filePath in Directory.GetFiles(sourceDir))
            {
                string fileName = Path.GetFileName(filePath);
                string destFilePath = Path.Combine(destDir, fileName);
                File.Copy(filePath, destFilePath, overwrite: true);
            }

            if (recursive)
            {
                foreach (string dirPath in Directory.GetDirectories(sourceDir))
                {
                    string dirName = Path.GetFileName(dirPath);
                    string destSubDir = Path.Combine(destDir, dirName);
                    CopyDirectory(dirPath, destSubDir, recursive);
                }
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Introduce la ruta del directorio de trabajo:");
            string source = Console.ReadLine()!;
            Console.WriteLine("Introduce la ruta de la carpeta para la copia de seguridad:");
            string dest = Console.ReadLine()!;

            try
            {
                CopyDirectory(source, dest, recursive: true);
                Console.WriteLine("¡Copia de seguridad creada con éxito!");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error al copiar: " + ex.Message);
            }
        }
    }
}

3. Matices útiles

Comparación: archivo vs directorio — tabla

Archivo Directorio
Método integrado
File.Copy
-
Orientado a objetos
FileInfo.CopyTo
-
Recorrido de anidamiento No requerido Requiere recursión
Creación de estructura Automáticamente Hay que crear la anidación manualmente
Peligro de recursión No Sí — no copiar "a sí mismo"

Aplicación práctica y ejercicios para hacer solo

Las tareas de copiar archivos y carpetas están por todas partes: desde backups hasta migraciones de datos o actualización de recursos en launchers de juegos. A menudo estos procesos se automatizan para evitar trabajo manual y el típico "¡oh, olvidé el archivo!" el lunes por la mañana.

Copiar directorios es necesario en escenarios donde hay que preservar no solo el contenido sino también la "estructura" del árbol, incluyendo subcarpetas, archivos anidados y configuraciones.

Checklist para copiar (para no meter la pata)

  • Existencia de la fuente
  • Existencia de la carpeta destino (crear si hace falta con Directory.CreateDirectory)
  • Política de sobreescritura de archivos — ¿necesitas overwrite: true?
  • ¿No estás copiando la carpeta "a sí misma"?
  • ¿No excedes la longitud de la ruta (especialmente en Windows)?
  • ¿Tienes en cuenta archivos ocultos / de sistema?

4. Particularidades y errores típicos al copiar

Permisos de acceso

El copiado puede fallar si tu programa no tiene permisos para leer los archivos fuente o escribir en el directorio destino. En ese caso recibirás una de las variantes de UnauthorizedAccessException. La solución — ejecutar el programa con privilegios de administrador (solo si realmente hace falta) o elegir carpetas normales para operar.

Archivos ocupados

Si un archivo está abierto en otra aplicación (por ejemplo, Excel lo tiene bloqueado), File.Copy puede lanzar una excepción. Asegúrate de que la aplicación que bloquea el archivo no interfiera, o implementa lógica de reintentos (try-catch con retries).

Sobreescritura de archivos

¿Qué política elegir: sobrescribir los archivos destino o dejarlos intactos si ya existen? Para backups suele tener sentido sobrescribir (overwrite: true), y para duplicar plantillas suele ser mejor no hacerlo (overwrite: false).

Copiar archivos ocultos y de sistema

Por defecto los métodos que hemos usado (Directory.GetFiles) devuelven todos los archivos, incluidos ocultos y de sistema. Si quieres saltártelos, filtra explícitamente:

foreach (string filePath in Directory.GetFiles(sourceDir))
{
    var attr = File.GetAttributes(filePath);
    if ((attr & FileAttributes.Hidden) == FileAttributes.Hidden)
        continue; // Saltamos archivos ocultos

    // Resto del código de copiado
}

Errores por rutas largas

Windows durante mucho tiempo limitó la longitud de la ruta a unos 260 caracteres. En versiones modernas se puede eliminar esa limitación, pero si trabajas con sistemas antiguos las rutas largas pueden ser un problema.

Enlaces simbólicos y "junctions"

En escenarios específicos en los directorios puedes encontrar enlaces simbólicos o "junctions". Los métodos normales de copiado pueden copiarlos como carpetas normales o ignorarlos. Para la mayoría de tareas de aprendizaje esto no importa, pero si trabajas con directorios del sistema — presta atención.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION