C #에서 디렉토리의 전체 내용을 복사하십시오.


524

C #에서 디렉토리의 전체 내용을 한 위치에서 다른 위치로 복사하고 싶습니다.

System.IO많은 재귀가없는 클래스를 사용 하여이 작업을 수행 할 수있는 방법이 없는 것 같습니다 .

VB에는 다음에 대한 참조를 추가 할 때 사용할 수있는 방법이 있습니다 Microsoft.VisualBasic.

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

이것은 다소 추악한 해킹처럼 보입니다. 더 좋은 방법이 있습니까?


101
나는 아래에 게시 된 대안을 살펴보면 VB 방식이 그렇게 추악하게 보이지 않는다고 말합니다.
Kevin Kershaw

41
.NET Framework의 일부인 경우 어떻게 해킹 할 수 있습니까? 코드 작성을 중단하고 얻은 것을 사용하십시오.
AMissico

15
그것은 일반적인 오해입니다. Microsft.VisualBasic에는 VB 코딩을 훨씬 쉽게하는 모든 일반적인 Visual Basic 프로 시저가 포함되어 있습니다. Microsot.VisualBasic.Compatibility는 VB6 레거시에 사용되는 어셈블리입니다.
AMissico

63
Microsoft.VisualBasic.Devices.Computer.FileSystem에는 2,000 줄 이상의 코드가 있습니다. CopyDirectory를 사용하면 상위 폴더를 하위 폴더 및 다른 검사로 복사하지 않습니다. 고도로 최적화되어 있습니다. 선택한 답변은 깨지기 쉬운 코드입니다.
AMissico

17
@AMissico은 - 좋아, 왜이 전체 코드에 최적화되어 Microsoft.VisualBasic하지 System.IO? 모노가 아닌 이유는 '핵심'으로 간주되는 모든 라이브러리가 있기 때문에 다른 모든 라이브러리 System.[something]는 그렇지 않기 때문입니다. 추가 DLL을 참조하는 데 아무런 문제가 없지만 Microsoft가이 기능을에 포함시키지 않은 이유는 충분합니다 System.IO.
Keith

답변:


553

훨씬 쉽게

//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
실제로 멋진 코드이지만 어디에서나 사용할 수있는 코드는 아닙니다. dirPath.Replace는 원하지 않는 결과를 초래할 수 있으므로 개발자는주의해야합니다. 인터넷을 통한 복사 및 붙여 넣기를 좋아하는 사람들에게 경고합니다. @jaysponsored가 게시 한 코드는 문자열을 사용하지 않기 때문에 안전합니다. 바꾸어도 코너 케이스가 있다고 확신합니다.
Alex

17
대상 디렉토리가 이미 존재하는 경우이 코드를 사용하면 예외가 발생하므로주의하십시오. 또한 이미 존재하는 파일을 덮어 쓰지 않습니다. 각 디렉토리를 만들기 전에 확인을 추가하고 File.Copy의 오버로드를 사용하여 대상 파일이 있으면 덮어 씁니다.
joerage

30
@Xaisoft - Replace인스턴스가 당신이 경로 내부의 반복 패턴이있는 경우 문제가 "sourceDir/things/sourceDir/things"될해야 "destinationDir/things/sourceDir/things"하지만, 당신이 그것을 대신 사용하면된다"destinationDir/things/destinationDir/things"
키스

35
*.*대신에 *? 확장자가없는 파일도 복사하고 싶지 않습니까?
Daryl

10
무언가를
만들어서

231

흠, 나는 그 질문을 오해하고 있다고 생각하지만 나는 그것을 위험에 빠뜨릴 것이다. 다음과 같은 간단한 방법으로 무엇이 잘못 되었습니까?

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

편집 이 게시물에 똑같이 간단한 질문에 대한 간단한 대답에 대한 인상적인 수의 다운 보트가 생겼으므로 설명을 추가하겠습니다. 제발 downvoting 전에 반드시 숙지 .

우선, 이 코드는 문제의 코드를 대체 하는 것으로 의도 된 것은 아닙니다 . 설명을 위해서만 사용하십시오.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectory이 답변에서 누락 된 몇 가지 추가 정확성 테스트 (예 : 소스 및 대상이 유효한 디렉토리인지, 소스가 대상의 부모인지 등)를 수행합니다. 이 코드는 아마도 더 최적화되었을 것입니다.

즉, 코드 가 잘 작동합니다 . 그것은 (거의 동일) 년 동안 성숙한 소프트웨어에 사용되었습니다. 모든 IO 처리에 따른 고유 한 결함 (예를 들어 코드를 작성하는 동안 사용자가 USB 드라이브를 수동으로 분리하면 어떻게됩니까?) 외에 알려진 문제는 없습니다.

특히, 여기서 재귀를 사용하는 것이 문제가되지 않는다는 것을 지적하고 싶습니다. 이론적으로 (개념적으로 가장 우아한 솔루션 임) 또는 실제로 는 아닙니다 . 이 코드는 스택을 오버플로하지 않습니다 . 스택은 깊이 중첩 된 파일 계층 구조를 처리 할 수있을만큼 충분히 큽니다. 스택 공간이 문제가되기 훨씬 전에 폴더 경로 길이 제한이 시작됩니다.

• 그래도주의 악의적 인 사용자가 하나 개의 문자 각각의 깊게 중첩 된 디렉토리를 사용하여이 가정을 파괴 할 수 있습니다. 나는 이것을 시도하지 않았습니다. 그러나 요점을 설명하기 위해 :이 코드를 일반적인 컴퓨터에서 오버플로하려면 디렉토리를 수천 번 중첩해야합니다 . 이것은 단순히 현실적인 시나리오가 아닙니다.


5
이것은 머리 재귀입니다. 디렉토리가 충분히 깊게 중첩되면 스택 오버플로가 될 수 있습니다.
spoulson

19
최근까지 OS에 의해 디렉토리 중첩 깊이가 제한되었습니다. 수백 번 이상 중첩되어있는 디렉토리가 있을지 의심 스럽다. 위의 코드는 훨씬 더 걸릴 수 있습니다 .
Konrad Rudolph

5
나는 재귀 접근법을 좋아한다. 스택 오버플로의 위험은 최악이다.
David Basarab

49
@ DTashkinov : 실례하지만 약간 과도하게 보입니다. 명백한 코드가 다운 보트 인 이유는 무엇입니까? 반대의 경우도 마찬가지입니다. 내장 방법이 이미 게시되었지만 Keith는 다른 방법을 구체적으로 요청했습니다 . 마지막 문장은 무엇을 의미합니까? 미안하지만, 난 당신의 downvoting 이유를 전혀 이해하지 못합니다.
Konrad Rudolph

6
@ AMissico : 무엇 보다 낫다 ? 아무도 프레임 워크의 VB 코드보다 낫다고 주장하지 않았습니다. 우리 는 그렇지 않다는 것을 알고 있습니다.
Konrad Rudolph

132

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
디렉토리가 존재하는지 확인할 이유가 없습니다. 디렉토리가 이미 존재하면 아무 것도하지 않는 Directoty.CreateDirectory를 호출하십시오.
탈 제롬

1
256자를 초과하는 경로를 처리하려는 경우 ZetaLongPaths
AK

2
이 답변은 그들 모두에게 가장 유용한 것 같습니다. 문자열 대신 DirectoryInfo를 사용하면 많은 잠재적 인 문제를 피할 수 있습니다.
DaedalusAlpha

50

이 시도:

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

xcopy 인수는 다를 수 있지만 아이디어를 얻습니다.


3
/ E는 모든 하위 디렉토리 (빈 디렉토리도 포함)를 복사하도록 지시합니다. / I는 대상이 존재하지 않으면 해당 이름으로 디렉토리를 만든다고 말합니다.
d4nt

6
안전을 위해 큰 따옴표를 추가하십시오.
jaysonragasa

6
기존 파일을 덮어 쓰라는 메시지가 표시되지 않도록 / Y를 추가하십시오. stackoverflow.com/q/191209/138938
Jon Crowell

16
죄송하지만 이건 끔찍 해요 대상 시스템이 창이라고 가정합니다. 향후 버전에는 해당 특정 경로에 xcopy.exe가 포함되어 있다고 가정합니다. xcopy의 매개 변수가 변경되지 않는다고 가정합니다. xcopy의 매개 변수를 문자열로 어셈블해야하므로 많은 오류 가능성이 있습니다. 또한 샘플은 시작된 프로세스의 결과에 대한 오류 처리를 언급하지 않습니다. 다른 방법과 달리 이는 자동으로 실패하기 때문입니다.
cel sharp

3
@ MatthiasJansen, 나는 당신이 그것을 매우 개인적인 것으로 생각합니다. 대답은 요점에 있으며 그것을 달성하는 방법에 대해 많이 설명합니다 ... 질문은 크로스 플랫폼 호환성을 요구하지 않거나 xcopy 또는 다른 것을 사용하지 않기 때문에 포스터는 이것이 어떻게 한 가지 방법으로 달성 될 수 있는지 설명하기 위해 대답했습니다 ... 똑같은 일을하는 1000 가지 방법이있을 수 있고 그에 대한 답변은 다를 수 있습니다. 그래서이 포럼은 연설을하기 위해 여기에 있으며 전 세계의 프로그래머들이 그들의 경험을 공유하기 위해 여기에 있습니다. 나는 당신의 의견에 투표를합니다.
KMX

47

또는 어려운 방향으로 가고 싶다면 Microsoft.VisualBasic 용 프로젝트에 대한 참조를 추가 한 후 다음을 사용하십시오.

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

그러나 재귀 함수 중 하나를 사용하는 것이 VB dll을로드 할 필요가 없으므로 더 좋은 방법입니다.


1
그것은 어쨌든 내가했던 방식과 다르지 않습니다 .VB의 이전 버전과 호환되는 항목을로드해야 여전히 할 수 있습니다.
Keith

10
VB 어셈블리를로드하는 데 비용이 많이 듭니까? VB 옵션은 C # 버전보다 훨씬 우아합니다.
jwmiller5

3
"VB의 이전 버전과의 호환성"은 무엇입니까? CopyDirectory는 셸 또는 프레임 워크를 사용합니다.
AMissico

3
나는 그것이 켜져 있기를 바란다 System.IO.Directory. 그러나 그것을 다시 쓰는 것보다 낫다!
Josh M.

2
이것은 다른 옵션들보다 훨씬 더 쉬운 방법입니다.
reggaeguitar

38

이 사이트는 항상 많은 도움을 받았고 이제 다른 사람들을 내가 아는 것을 도울 차례입니다.

아래 코드가 누군가에게 유용하기를 바랍니다.

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
후행 백 슬래시를 기억하십시오
Alexey F

24
여러분,을 사용하십시오 Path.Combine(). 문자열 연결을 사용하여 파일 경로를 함께 사용하지 마십시오.
Andy

3
위 코드 스 니펫에 OBOB가 있습니다. 사용 source_dir.Length + 1하지 않아야 source_dir.Length합니다.
PellucidWombat

이 코드는 좋은 개념이지만 ... 파일에 "."이 없어도됩니다. 그 안에 ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories)를 사용하는 것이 좋습니다.
Jean Libera

@JeanLibera에게 감사합니다. 귀하의 제안으로 코드를 변경했습니다.
jaysponororor

14

스택 오버플로를 피하기 위해 재귀없이 폴더를 재귀 적으로 복사하십시오.

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

유용한 비 재귀 템플릿 :)
Minh Nguyen

2
경로 제한
Ed S.

5

다음은 이와 같은 IO 작업에 사용한 유틸리티 클래스입니다.

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

Microsoft는 Microsoft.VisualBasic에 대해 내부적으로 SHFileOperation을 사용합니다.
jrh

3

성능을 인식하지 못할 수도 있지만 30MB 폴더에 사용하고 있으며 완벽하게 작동합니다. 또한, 나는 쉬운 작업에 필요한 모든 양의 코드와 재귀를 좋아하지 않았습니다.

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

참고 : ZipFile은 System.IO.Compression 네임 스페이스의 .NET 4.5 이상에서 사용할 수 있습니다.


1
나도 질문이 아니지만 선택한 대답은 재귀가 필요하지 않습니다. 이 답변은 디스크에 zip 파일을 생성하는데, 이는 파일 복사를위한 많은 추가 작업입니다. 데이터의 추가 복사본을 생성 할뿐만 아니라 프로세서 압축 및 압축 해제에 시간을 소비하고 있습니다. 나는 그것이 당신의 신발로 못을 박을 수있는 것과 같은 방식으로 작동한다고 확신하지만, 더 좋은 방법이 있지만 잘못 될 수있는 더 많은 일을하는 것이 더 좋습니다.
Keith

내가 이것으로 끝내는 이유는 문자열 교체입니다. 다른 사람들이 지적했듯이, 받아 들여진 대답은 많은 우려를 나타냅니다. 확장명이나 이름이없는 폴더 패턴이나 파일을 반복 할뿐만 아니라 정션 링크가 작동하지 않을 수 있습니다. 코드가 적고 잘못 될 가능성이 적습니다. 그리고 프로세서 시간은 저에게는 관심사가 아니기 때문에 특정 상황에 적합합니다.
AlexanderD

2
예, 신호등 하나를 피하기 위해 1000 마일을 달리는 것과 같습니다. 그러나 그것은 여정입니다. 폴더 패턴을 확인하는 것은 ZIP 아래에서 ZIP이 수행 해야하는 작업과 비교하여 사소한 것입니다. 프로세서, 디스크, 전기를 낭비하지 않거나 동일한 컴퓨터에서 다른 프로그램과 함께 실행 해야하는 곳에 관심이있는 사람에게는 강력히 권장합니다. 혹시 인터뷰에서 질문의 유형을 묻는 경우에도 결코 "나는 프로세서 시간을 걱정하지 않아도 내 코드는 간단하다"로 갈 - 당신이 일을받지 않습니다.
Keith

1
@ justin-r에서 제공 한 답변으로 전환했습니다 . 그래도이 답변을 다른 방법으로 남겨 두겠습니다
AlexanderD

1
폴더가 별도의 네트워크 공유에 있고 많은 파일을 포함하고 있다면 이것이 최선의 선택이라고 생각합니다.
Danny Parker

2

서버 및 개발 시스템에서 작업하는 경우 오류를 확인하고 xcopy 경로를 변경할 필요가 없으므로 d4nt의 대답이 약간 개선되었습니다.

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

이것은 내 코드가 도움이되기를 바랍니다.

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

SearchOption폴더 및 파일 검색 에서 플래그를 사용하여 선택한 답변 을 4 줄의 코드로 표시하십시오. 또한 .HasFlag열거 형 에서 확장 프로그램을 지금 확인하십시오 .
키스

2

Konrad의 인기있는 답변을 좋아하지만 source폴더 아래에 하위 폴더를 target두지 않고 폴더 아래에 폴더 를 만들고 싶다면 target여기에 코드가 있습니다. 새로 생성 된을 반환 DirectoryInfo합니다.

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

Microsoft 웹 사이트에서 가져온 를 언제든지 사용할 수 있습니다 .

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
이 줄 file.CopyTo(temppath, false);은 "이 파일을 존재하지 않는 경우에만이 파일을 복사하십시오"라는 문구를 명심하십시오. 대부분의 시간은 우리가 원하는 것이 아닙니다. 그러나 왜 그것이 기본값인지 이해할 수 있습니다. 파일 덮어 쓰기 방법에 플래그를 추가 할 수 있습니다.
Andy

2

tboswell은 Proof 버전을 대체합니다 (파일 경로에서 반복되는 패턴에 탄력적 임)

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
여러분,을 사용하십시오 Path.Combine(). 문자열 연결을 사용하여 파일 경로를 함께 사용하지 마십시오.
Andy

2

내 솔루션은 기본적으로 @Termininja의 답변을 수정 한 것이지만 조금 향상 시켰으며 허용되는 답변보다 5 배 이상 빠릅니다.

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

편집 : @Ahmed Sabry를 전체 병렬 foreach로 수정하면 더 나은 결과를 얻을 수 있지만 코드는 재귀 함수를 사용하며 일부 상황에서는 이상적이지 않습니다.

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

이전 코드에 대해 죄송하지만 여전히 버그가 있습니다.

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

다음은 FileInfo에 대한 확장 메소드입니다. FileInfo.CopyTo ( 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;
}

1

이 수업을 사용하십시오.

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
이것은 다른 답변과 비슷하며 .ToList().ForEach((직접 디렉토리를 열거하는 것보다 약간 더 많은 작업, 메모리 및 약간 느린 ) 리팩토링 된 확장 방법입니다. 선택한 답변은 SearchOption.AllDirectories재귀를 사용 하고 피하기 때문에 해당 모델로 전환하는 것이 좋습니다. 또한 확장 방법에는 일반적으로 유형의 이름이 필요하지 않습니다. 이름이 CopyTo()바뀌도록 이름을 바 sourceDir.CopyTo(destination);
Keith

1

모든 폴더와 파일을 복사하기위한 루프가 하나만있는 변형 :

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

을 사용하려는 경우 식 구성 (특히 Windows 경로 구분 기호 고려)의 일부로 Regex사용해야합니다 Regex.Escape(path). 당신은 또한 생성 (그리고 아마도 컴파일)이로부터 혜택을받을 new Regex()루프의 객체 외부보다는 정적 인 방법에 의존.
jimbobmcgee 2014

0

코드보다 우수 (재귀가있는 DirectoryInfo 확장 방법)

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
나는 이것이 재귀 (필요하지 않은 곳)를 사용하고 디버깅을 더 어렵게 만드는 예외를 숨기는 것 외에는 받아 들여진 대답에 무엇이 추가되는지 확실하지 않습니다.
Keith

0

폴더의 모든 파일을 복사 및 교체

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

답변을 응원하지만 이것이 무엇을 추가하는지 잘 모르겠습니다. 또한 try catch throw무의미합니다.
Keith

0

아래 코드는 마이크로 소프트의 제안 인 방법으로 복사 디렉토리 와이 사랑에 의해 공유 @iato 하지만 단지 복사 서브 디렉토리와 재귀 적으로 소스 폴더의 파일 과는 그 자체 폴더 소스를 복사하지 않습니다 마우스 오른쪽 버튼으로 클릭처럼 (-> 사본 ).

그러나이 답변 아래 에는 까다로운 방법 이 있습니다.

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

소스 폴더와 하위 폴더의 내용 을 재귀 적 으로 복사 하려면 다음과 같이 간단하게 사용할 수 있습니다.

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

그러나 소스 디렉토리를 자동 으로 복사 하려면 (소스 폴더를 마우스 오른쪽 버튼으로 클릭하고 붙여 넣기를 클릭 한 대상 폴더에서 복사를 클릭 한 것과 비슷한) 다음과 같이 사용해야합니다.

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

이미 아래에 답변을 올렸습니다 : stackoverflow.com/a/45199038/1951524
Martin Schneider

@ MA-Maddin에게 감사하지만 소스 폴더 자체를 복사합니까? 아니면 내용 만?
Arash.Zandi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.