Copie todo o conteúdo de um diretório em C #


524

Quero copiar todo o conteúdo de um diretório de um local para outro em C #.

Não parece haver uma maneira de fazer isso usando System.IOclasses sem muita recursão.

Existe um método no VB que podemos usar se adicionarmos uma referência a Microsoft.VisualBasic:

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

Parece um truque feio. Existe uma maneira melhor?


101
Eu diria que, olhando para as alternativas postadas abaixo, a maneira VB não parece tão feia.
22668 Kevin Kershaw

41
Como pode ser um hack quando faz parte do .NET Framework? Pare de escrever código e use o que você tem.
AMissico

15
Esse é um equívoco comum. Microsft.VisualBasic contém todos os procedimentos comuns do Visual Basic que facilitam muito a codificação no VB. Microsot.VisualBasic.Compatibility é o assembly usado para o legado do VB6.
31720ississico #

63
Existem mais de 2.000 linhas de código no Microsoft.VisualBasic.Devices.Computer.FileSystem. O CopyDirectory garante que você não esteja copiando uma pasta pai para uma pasta filha e outras verificações. É altamente otimizado, e assim por diante. A resposta selecionada é um código frágil, na melhor das hipóteses.
AMissico

17
@ AMissico - ok, então por que esse código otimizado e completo está dentro Microsoft.VisualBasice não System.IO? A razão pela qual não está no Mono é porque todas as bibliotecas consideradas 'essenciais' são System.[something]- todas as outras não são. Não tenho nenhum problema ao referenciar uma DLL extra, mas há uma boa razão pela qual a Microsoft não incluiu esse recurso System.IO.
Keith

Respostas:


553

Muito facil

//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
É realmente um bom código, mas esse não é o tipo de código que pode ser usado em qualquer lugar. Os desenvolvedores devem ter cuidado, pois dirPath.Replace pode causar consequências indesejadas. Apenas um aviso para pessoas que gostam de copiar e colar na rede. O código postado por @jaysponsored é mais seguro porque não usa string.Replace, mas tenho certeza de que também possui seus casos de canto.
Alex

17
Tenha cuidado com este código, pois ele lançará uma exceção se o diretório de destino já existir. Também não substituirá arquivos que já existem. Basta adicionar uma verificação antes de criar cada diretório e usar a sobrecarga de File.Copy para substituir o arquivo de destino, se existir.
Joerage

30
@Xaisoft - Replacetem um problema se você tem um padrão de repetição dentro do caminho, por exemplo "sourceDir/things/sourceDir/things"deve tornar-se "destinationDir/things/sourceDir/things", mas se você usar substituí-lo torna-se"destinationDir/things/destinationDir/things"
Keith

35
Por que ao *.*invés de *? Você também não deseja copiar arquivos sem extensões?
Daryl

10
Vamos construir algo e contribuir para o Open Source .NET Núcleo ...: /
MZN

231

Hmm, acho que não entendi a pergunta, mas vou arriscar. O que há de errado com o seguinte método direto?

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 Como esta postagem recebeu um número impressionante de votos negativos por uma resposta tão simples a uma pergunta igualmente simples, deixe-me acrescentar uma explicação. Por favor, leia isso antes da votação .

Primeiro de tudo, esse código não pretende ser um substituto para o código da pergunta. É apenas para fins ilustrativos.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectoryfaz alguns testes adicionais de correção (por exemplo, se a origem e o destino são diretórios válidos, se a origem é um pai do destino, etc.) que estão faltando nesta resposta. Esse código provavelmente também é mais otimizado.

Dito isto, o código funciona bem . Ele tem (quase idêntica) foi usado em um software maduro durante anos. Além da inconstância inerente presente em todos os manipuladores de E / S (por exemplo, o que acontece se o usuário desconectar manualmente a unidade USB enquanto seu código estiver gravando nela?), Não há problemas conhecidos.

Em particular, gostaria de salientar que o uso da recursão aqui não é absolutamente um problema. Nem na teoria (conceitualmente, é a solução mais elegante) nem na prática: esse código não sobrecarregará a pilha . A pilha é grande o suficiente para lidar com hierarquias de arquivos aninhadas profundamente. Muito antes de o espaço da pilha se tornar um problema, a limitação de comprimento do caminho da pasta entra em ação.

Observe que um usuário mal - intencionado pode quebrar essa suposição usando diretórios profundamente aninhados de uma letra cada. Eu não tentei isso. Mas apenas para ilustrar o ponto: para fazer esse código transbordar em um computador típico, os diretórios precisariam ser aninhados alguns milhares de vezes. Este simplesmente não é um cenário realista.


5
Isso é recursão na cabeça. Pode ser vítima de um estouro de pilha se os diretórios estiverem aninhados o suficiente.
spoulson 12/09/08

19
Até muito recentemente, a profundidade do aninhamento de diretório era restrita pelo sistema operacional. Duvido que você encontre diretórios aninhados mais de algumas centenas de vezes (se for o caso). O código acima pode demorar muito mais.
319 Konrad Rudolph

5
Gosto da abordagem recursiva, o risco de um estouro de pilha é mínimo, na pior das hipóteses.
David Basarab

49
@ DTashkinov: bem, desculpe-me, mas isso parece um pouco excessivo. Por que código óbvio == voto negativo? O oposto deve ser verdadeiro. O método interno já havia sido publicado, mas Keith solicitou especificamente outro método. Além disso, o que você quer dizer com sua última frase? Desculpe, mas eu simplesmente não entendo seus motivos de voto negativo.
22911 Konrad Rudolph

6
@ AMissico: melhor do que o que ? Ninguém afirmou que era melhor que o código VB do framework. Nós sabemos que não é.
21911 Konrad Rudolph

132

Copiado do 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
Não há motivo para verificar se o diretório existe, basta chamar Directoty.CreateDirectory, que não fará nada se o diretório já existir.
21815 Tal Jerome

1
Para aqueles que procuram lidar com caminhos mais de 256 caracteres, você pode usar um pacote Nuget chamados ZetaLongPaths
AK

2
Essa resposta parece ser a mais útil de todas. Ao usar o DirectoryInfo em vez de cadeias, muitos problemas em potencial são evitados.
DaedalusAlpha

50

Tente o seguinte:

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

Seus argumentos xcopy podem variar, mas você entendeu.


3
/ E diz para copiar todos os subdiretórios (mesmo os vazios). / Eu digo que, se o destino não existir, crie um diretório com esse nome.
d4nt

6
adicione aspas duplas para estar seguro.
Jaysonragasa

6
Adicione / Y para evitar ser solicitado a substituir arquivos existentes. stackoverflow.com/q/191209/138938
Jon Crowell

16
Desculpe, mas isso é horrível. Ele assume que o sistema de destino é o Windows. Ele pressupõe que versões futuras incluem xcopy.exe nesse caminho específico. Assume que os parâmetros do xcopy não mudam. É necessário montar os parâmetros para xcopy como string, o que apresenta bastante potencial de erro. Além disso, o exemplo não menciona nenhum tratamento de erros para os resultados do processo iniciado, o que eu esperaria, porque, ao contrário de outros métodos, isso falharia silenciosamente.
cel afiada

3
@MatthiasJansen, acho que você tomou isso muito pessoal. A resposta é direta e explica muito sobre como alcançá-lo ... Como a pergunta não exige compatibilidade entre plataformas ou não usa xcopy ou qualquer outra coisa que o pôster respondeu para explicar como isso pode ser alcançado de uma maneira ... pode haver 1000 maneiras de fazer a mesma coisa e as respostas variam .. é por isso que este fórum está aqui para abordar e programadores em todo o mundo vêm aqui para compartilhar suas experiências. Voto seu comentário.
KMX

47

Ou, se desejar seguir o caminho mais difícil, adicione uma referência ao seu projeto para Microsoft.VisualBasic e use o seguinte:

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

No entanto, o uso de uma das funções recursivas é o melhor caminho a percorrer, pois não será necessário carregar a DLL do VB.


1
Isso não é realmente diferente do que eu fiz de qualquer maneira - você ainda precisa carregar o material de compatibilidade com versões anteriores do VB para poder fazer isso.
Keith

10
O carregamento do conjunto VB é caro? As opções de VB são muito mais elegantes que as versões em C #.
jwmiller5

3
O que "coisas de compatibilidade com versões anteriores do VB"? O CopyDirectory usa o Shell ou o Framework.
AMissico

3
Eu gostaria que estivesse ligado System.IO.Directory, mas é melhor do que reescrevê-lo!
Josh M.

2
Este é o caminho a percorrer imo, muito mais fácil do que qualquer uma das outras opções
reggaeguitar

38

Este site sempre me ajudou muito, e agora é minha vez de ajudar os outros com o que eu sei.

Espero que meu código abaixo seja útil para alguém.

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
Lembre-se da barra invertida à direita #
Alexey F

24
Gente, use Path.Combine(). Nunca use concatenação de cadeias para montar caminhos de arquivo.
Andy

3
Você tem um OBOB no snippet de código acima. Você deveria estar usando source_dir.Length + 1, não source_dir.Length.
PellucidWombat

Este código é um bom conceito, mas ... Um arquivo não precisa ter um "." nele, portanto, seria melhor usar ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories))
Jean Libera

Obrigado @ JeanLibera, você está certo. Mudei o código com sua sugestão.
jaysponsored

14

Copie a pasta recursivamente sem recursão para evitar o estouro da pilha.

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

modelo útil não-recursão :)
Minh Nguyen

2
Difícil imaginar soprando a pilha antes brilhando o limite caminho
Ed S.

5

Aqui está uma classe de utilitário que eu usei para tarefas de E / S 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;
        }
    }
}

Observe que a Microsoft usa SHFileOperation internamente para Microsoft.VisualBasic.
JRH

3

Pode não ser sensível ao desempenho, mas estou usando-o para pastas de 30 MB e funciona perfeitamente. Além disso, eu não gostei de toda a quantidade de código e recursão necessária para uma tarefa tão 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á disponível no .NET 4.5+ no espaço para nome System.IO.Compression


1
Nem eu, daí a pergunta, mas a resposta selecionada não precisa de recursão. Essa resposta cria um arquivo zip no disco, que é um trabalho adicional para uma cópia de arquivo - você não apenas cria uma cópia adicional dos dados, mas também gasta o tempo do processador compactando e descompactando. Tenho certeza de que funciona, da mesma maneira que você provavelmente pode pregar um prego com o sapato, mas é mais trabalho com mais coisas que podem dar errado, enquanto existem maneiras melhores de fazê-lo.
217 Keith

A razão pela qual acabei com isso é a substituição de cadeias. Como outros já apontaram, a resposta aceita apresenta muitas preocupações; o link de junção pode não funcionar, além de repetir o padrão ou os arquivos da pasta sem extensão ou nome. Menos código, menos chance de dar errado. E desde o tempo do processador não é uma preocupação para mim, torna-se adequado para o meu caso específico
Alexanderd

2
Sim, é como dirigir 1000 milhas fora do seu caminho para evitar um único semáforo, mas é a sua jornada, então vá em frente. A verificação de padrões de pasta é trivial em comparação com o que o ZIP precisa fazer sob o capô. Eu recomendo fortemente isso para quem se preocupa em não desperdiçar processador, disco, eletricidade ou onde isso precisa ser executado ao lado de outros programas na mesma máquina. Além disso, se você já fez esse tipo de pergunta na entrevista, nunca vá com "meu código é simples, portanto não me importo com o tempo do processador" - você não conseguirá o emprego.
21418 Keith

1
Mudei para a resposta fornecida por @ justin-r . Ainda assim, eu vou deixar esta resposta lá como apenas uma outra maneira de fazê-lo
Alexanderd

1
Se as pastas estiverem em compartilhamentos de rede separados e contiverem muitos arquivos, essa seria a melhor opção na minha opinião.
Danny Parker

2

Uma pequena melhoria na resposta do d4nt, pois você provavelmente deseja verificar se há erros e não precisa alterar os caminhos do xcopy se estiver trabalhando em um servidor e uma máquina de desenvolvimento:

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

Este é o meu código espero que isso ajude

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

Veja a resposta selecionada, usando o SearchOptionsinalizador nas pesquisas de pastas e arquivos, ele faz isso em 4 linhas de código. Verifique também a .HasFlagextensão agora em enums.
Keith

2

Se você gosta da resposta popular do Konrad, mas deseja que sourceela seja uma pasta target, em vez de colocar seus filhos na targetpasta, aqui está o código para isso. Ele retorna o recém-criado DirectoryInfo, o que é ú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;
}

2

Você sempre pode usar isso , retirado do site da 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
Isso é ótimo - lembre-se de que a linha file.CopyTo(temppath, false);diz "copie este arquivo para este local, apenas se ele não existir", que na maioria das vezes não é o que queremos. Mas, eu posso entender por que o padrão é isso. Talvez adicione um sinalizador ao método para substituir arquivos.
Andy

2

tboswell substitui a versão Proof (que é resiliente à repetição do padrão no caminho do arquivo)

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, use Path.Combine(). Nunca use concatenação de cadeias para montar caminhos de arquivo.
Andy

2

Minha solução é basicamente uma modificação da resposta do @ Termininja, no entanto eu a aprimorei um pouco e parece ser mais de 5 vezes mais rápida que a resposta aceita.

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

Edição: Modificar @Ahmed Sabry para foreach paralelo completo produz um resultado melhor, no entanto, o código usa a função recursiva e não é ideal em algumas situações.

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

Desculpe pelo código anterior, ele ainda tinha bugs :( (foi vítima do problema mais rápido da arma). Aqui ele é testado e está funcionando. A chave é o SearchOption.AllDirectories, que elimina a necessidade de recursão 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);
    }
}

1

Aqui está um método de extensão para DirectoryInfo no FileInfo.CopyTo (observe o 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;
}

1

Use essa 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
Isso é semelhante a outras respostas, refatoradas para uso .ToList().ForEach((que é um pouco mais de trabalho, memória e um pouco mais lenta do que apenas enumerar os diretórios diretamente) e como um método de extensão. A resposta selecionada usa SearchOption.AllDirectoriese evita recursão, portanto, recomendo mudar para esse modelo. Além disso, você geralmente não precisa do nome do tipo em métodos de extensão - eu renomeá-lo para CopyTo()modo que se tornousourceDir.CopyTo(destination);
Keith

1

Uma variante com apenas um loop para copiar todas as pastas e arquivos:

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 você for usar Regex, provavelmente também deve fazer Regex.Escape(path)parte da composição da sua expressão (especialmente considerando o separador de caminho do Windows). Você também pode se beneficiar da criação (e talvez compilação) de um new Regex()objeto fora do loop, em vez de confiar no método estático.
Jimbobmcgee #

0

Melhor que qualquer código (método de extensão para DirectoryInfo com recursão)

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
Não sei ao certo o que isso acrescenta à resposta aceita, além de usar recursão (onde isso não precisa) e ocultar exceções para dificultar a depuração.
Keith

0

Copie e substitua todos os arquivos da pasta

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

Felicidades pela resposta, mas não tenho certeza do que isso acrescenta. Também o try catch throwé inútil.
Keith

0

O código abaixo é um exemplo de como copiar diretórios da Microsoft e é compartilhado pelo querido @iato, mas apenas copia subdiretórios e arquivos da pasta de origem recursivamente e não copia a pasta de origem por si próprio (como clique com o botão direito do mouse -> copiar )

mas há uma maneira complicada abaixo desta resposta:

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 você deseja copiar o conteúdo da pasta e subpastas de origem recursivamente, basta usá-lo da seguinte maneira:

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

mas se você deseja copiar o diretório de origem por si próprio (semelhante ao clique com o botão direito do mouse na pasta de origem e ao clicar em copiar, na pasta de destino em que você clicou em colar), use o seguinte:

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

já foi publicado com algumas respostas abaixo: stackoverflow.com/a/45199038/1951524
Martin Schneider

Obrigado @ MA-Maddin, mas copia a própria pasta de origem? ou apenas o conteúdo?
Arash.Zandi
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.