programing

C# 디렉터리의 전체 내용 복사

kakaobank 2023. 5. 7. 11:46
반응형

C# 디렉터리의 전체 내용 복사

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

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

VB에 참조를 추가하면 사용할 수 있는 방법이 있습니다.Microsoft.VisualBasic:

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

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

훨씬 쉽습니다.

private static void CopyFilesRecursively(string sourcePath, string targetPath)
{
    //Now Create all of the directories
    foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
    {
        Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
    }

    //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, targetPath), true);
    }
}

흠, 제가 질문을 잘못 이해한 것 같은데 모험을 해보겠습니다.다음과 같은 간단한 방법의 문제점은 무엇입니까?

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

편집 이 게시물은 똑같이 간단한 질문에 대한 간단한 답변으로 많은 다운 투표를 받았으므로 설명을 추가합니다.다운로드 투표 전에 이 내용을 읽어보시기 바랍니다.

우선, 이 코드는 문제의 코드를 대체하기 위한 것이 아닙니다.이것은 단지 설명을 위한 것입니다.

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

그렇긴 하지만, 코드는 잘 작동합니다.수년간 성숙한 소프트웨어에서 (거의 동일하게)모든 IO 처리에 내재된 변동성(예: 코드를 쓰는 동안 사용자가 USB 드라이브의 플러그를 수동으로 뽑으면 어떻게 됩니까?)을 제외하고는 알려진 문제가 없습니다.

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

악의적인 사용자는 각 문자 하나씩의 중첩된 디렉토리를 사용하여 이러한 가정을 깰 수 있습니다.이거 안 먹어봤어요.하지만 요점을 설명하자면, 일반적인 컴퓨터에서 이 코드가 오버플로되도록 하려면 디렉터리가 수천 번 중첩되어야 합니다.이것은 단순히 현실적인 시나리오가 아닙니다.

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

또는 힘든 길을 가고 싶다면 Microsoft용 프로젝트에 참조 자료를 추가하십시오.Visual Basic을 선택한 다음 다음을 사용합니다.

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

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

사용해 보십시오.

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 인수는 다양할 수 있지만 아이디어는 이해할 수 있습니다.

이 사이트는 항상 저에게 많은 도움을 주었고, 이제 제가 아는 것을 다른 사람들에게 도움을 줄 차례입니다.

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

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

스택 오버플로를 방지하기 위해 재귀 없이 폴더를 재귀적으로 복사합니다.

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

여기 제가 이런 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;
        }
    }
}

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

저의 솔루션은 기본적으로 @Terminja의 답변을 수정한 것이지만, 제가 그것을 조금 향상시켰으며 승인된 답변보다 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를 완전 병렬로 수정하면 더 나은 결과가 나오지만, 코드는 재귀 함수를 사용하고 일부 상황에서는 이상적이지 않습니다.

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

다음은 간결하고 효율적인 솔루션입니다.

namespace System.IO {
  public static class ExtensionMethods {

    public static void CopyTo(this DirectoryInfo srcPath, string destPath) {
      Directory.CreateDirectory(destPath);
      Parallel.ForEach(srcPath.GetDirectories("*", SearchOption.AllDirectories), 
        srcInfo => Directory.CreateDirectory($"{destPath}{srcInfo.FullName[srcPath.FullName.Length..]}"));
      Parallel.ForEach(srcPath.GetFiles("*", SearchOption.AllDirectories), 
        srcInfo => File.Copy(srcInfo.FullName, $"{destPath}{srcInfo.FullName[srcPath.FullName.Length..]}", true));
      });
    }

  }
}

사용 방법:

new DirectoryInfo(sourcePath).CopyTo(destinationPath);

성능 인식이 아닐 수도 있지만 30MB 폴더에 사용하고 있는데 완벽하게 작동합니다.게다가, 저는 그렇게 쉬운 작업에 필요한 모든 양의 코드와 재귀를 좋아하지 않았습니다.

var src = "c:\src";
var dest = "c:\dest";
var cmp = CompressionLevel.NoCompression;
var zip = source_folder + ".zip";

ZipFile.CreateFromDirectory(src, zip, cmp, includeBaseDirectory: false);
ZipFile.ExtractToDirectory(zip, dest_folder);

File.Delete(zip);

참고: ZipFile은 에서 사용할 수 있습니다.시스템에 NET 4.5+가 있습니다.IO.압축 네임스페이스

서버 및 개발 컴퓨터에서 작업하는 경우 오류를 확인하고 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));
    }
}

이것은 나의 코드입니다. 이것이 도움이 되길 바랍니다.

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

콘라드의 있는 대답을 , 만약당이콘인의기있좋대만아지, ▁the▁if면,▁you▁want다원한▁kon▁answers당,rad'신▁like▁but.source 자체가 아폴다래니더입의그가체자▁▁folder 아래의 폴더가 .target아이들을 아래에 두는 것보다.target폴더, 여기 코드가 있습니다. 만든 ▁the다니를 반환합니다.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;
}

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

이전 코드에 대해 미안하지만, 여전히 버그가 있었습니다 :( (가장 빠른 총 문제의 먹잇감이 되었습니다.여기서 테스트되고 작동합니다.핵심은 검색 옵션입니다.모든 디렉토리 - 명시적인 재귀가 필요하지 않습니다.

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

디렉토리에 대한 확장 방법은 다음과 같습니다.파일 정보를 제공합니다.복사 대상(참고: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;
}

이 클래스를 사용합니다.

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

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

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

다른 코드보다 우수(디렉토리 확장 메서드)재귀가 있는 정보

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

폴더의 모든 파일 복사 및 바꾸기

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

아래 코드는 마이크로소프트 제안 복사 방법 디렉터리이며 dear @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));

아래 코드는 동일한 폴더 구조에서 주어진 패턴의 소스에서 대상으로 모든 파일을 복사합니다.

public static void Copy()
        {
            string sourceDir = @"C:\test\source\";
            string destination = @"C:\test\destination\";

            string[] textFiles = Directory.GetFiles(sourceDir, "*.txt", SearchOption.AllDirectories);

            foreach (string textFile in textFiles)
            {
                string fileName = textFile.Substring(sourceDir.Length);
                string directoryPath = Path.Combine(destination, Path.GetDirectoryName(fileName));
                if (!Directory.Exists(directoryPath))
                    Directory.CreateDirectory(directoryPath);

                File.Copy(textFile, Path.Combine(directoryPath, Path.GetFileName(textFile)), true);
            }
        }

여기에 이미지 설명 입력

그냥 내 버전을 추가하고 싶었어요.디렉터리와 파일을 모두 처리할 수 있으며 대상 파일이 있는 경우 덮어쓰거나 건너뛸 수 있습니다.

public static void Copy(
    string source,
    string destination,
    string pattern = "*",
    bool includeSubFolders = true,
    bool overwrite = true,
    bool overwriteOnlyIfSourceIsNewer = false)
{
    if (File.Exists(source))
    {
        // Source is a file, copy and leave
        CopyFile(source, destination);
        return;
    }

    if (!Directory.Exists(source))
    {
        throw new DirectoryNotFoundException($"Source directory does not exists: `{source}`");
    }

    var files = Directory.GetFiles(
        source,
        pattern,
        includeSubFolders ?
            SearchOption.AllDirectories :
            SearchOption.TopDirectoryOnly);

    foreach (var file in files)
    {
        var newFile = file.Replace(source, destination);
        CopyFile(file, newFile, overwrite, overwriteOnlyIfSourceIsNewer);
    }
}

private static void CopyFile(
    string source,
    string destination,
    bool overwrite = true,
    bool overwriteIfSourceIsNewer = false)
{
    if (!overwrite && File.Exists(destination))
    {
        return;
    }

    if (overwriteIfSourceIsNewer && File.Exists(destination))
    {
        var sourceLastModified = File.GetLastWriteTimeUtc(source);
        var destinationLastModified = File.GetLastWriteTimeUtc(destination);
        if (sourceLastModified <= destinationLastModified)
        {
            return;
        }

        CreateDirectory(destination);
        File.Copy(source, destination, overwrite);
        return;
    }

    CreateDirectory(destination);
    File.Copy(source, destination, overwrite);
}

private static void CreateDirectory(string filePath)
{
    var targetDirectory = Path.GetDirectoryName(filePath);
    if (targetDirectory != null && !Directory.Exists(targetDirectory))
    {
        Directory.CreateDirectory(targetDirectory);
    }
}

이 코드의 속성:

  • 병렬 작업이 없으므로 성능이 떨어지지만 파일을 하나씩 처리하여 로그를 기록하거나 중지할 수 있습니다.
  • 숨겨진 파일을 건너뛸 수 있습니다.
  • 수정된 날짜까지 건너뛸 수 있습니다.
  • 선택한 파일 복사 오류로 인해 손상될 수 있음
  • SMB용으로 64K의 버퍼 사용 및FileShare.ReadWrite자물쇠를 피하기 위해
  • 예외 메시지 개인 설정
  • Windows의 경우

메모들
ExceptionToString()내부 예외 및 디스플레이 스택을 가져오려는 개인 확장입니다.다음으로 대체ex.Message또는 다른 코드.
log4net.ILog _log저는 ==Log4net== 다른 방법으로 로그를 작성할 수 있습니다.

/// <summary>
/// Recursive Directory Copy
/// </summary>
/// <param name="fromPath"></param>
/// <param name="toPath"></param>
/// <param name="continueOnException">on error, continue to copy next file</param>
/// <param name="skipHiddenFiles">To avoid files like thumbs.db</param>
/// <param name="skipByModifiedDate">Does not copy if the destiny file has the same or more recent modified date</param>
/// <remarks>
/// </remarks>
public static void CopyEntireDirectory(string fromPath, string toPath, bool continueOnException = false, bool skipHiddenFiles = true, bool skipByModifiedDate = true)
{
    log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    string nl = Environment.NewLine;

    string sourcePath = "";
    string destPath = "";
    string _exMsg = "";

    void TreateException(Exception ex)
    {
        _log.Warn(_exMsg);
        if (continueOnException == false)
        {
            throw new Exception($"{_exMsg}{nl}----{nl}{ex.ExceptionToString()}");
        }
    }

    try
    {
        foreach (string fileName in Directory.GetFileSystemEntries(fromPath, "*", SearchOption.AllDirectories))
        {
            sourcePath = fileName;
            destPath = Regex.Replace(fileName, "^" + Regex.Escape(fromPath), toPath);

            Directory.CreateDirectory(Path.GetDirectoryName(destPath));
            
            _log.Debug(FileCopyStream(sourcePath, destPath,skipHiddenFiles,skipByModifiedDate));
        }
    }
    // Directory must be less than 148 characters, File must be less than 261 characters
    catch (PathTooLongException)
    {
        throw new Exception($"Both paths must be less than 148 characters:{nl}{sourcePath}{nl}{destPath}");
    }
    // Not enough disk space. Cancel further copies
    catch (IOException ex) when ((ex.HResult & 0xFFFF) == 0x27 || (ex.HResult & 0xFFFF) == 0x70)
    {
        throw new Exception($"Not enough disk space:{nl}'{toPath}'");
    }
    // used by another process
    catch (IOException ex) when ((uint)ex.HResult == 0x80070020)
    {
        _exMsg = $"File is being used by another process:{nl}'{destPath}'{nl}{ex.Message}";
        TreateException(ex);
    }
    catch (UnauthorizedAccessException ex)
    {
        _exMsg = $"Unauthorized Access Exception:{nl}from:'{sourcePath}'{nl}to:{destPath}";
        TreateException(ex);
    }
    catch (Exception ex)
    {
        _exMsg = $"from:'{sourcePath}'{nl}to:{destPath}";
        TreateException(ex);
    }
}

/// <summary>
/// File Copy using Stream 64K and trying to avoid locks with fileshare
/// </summary>
/// <param name="sourcePath"></param>
/// <param name="destPath"></param>
/// <param name="skipHiddenFiles">To avoid files like thumbs.db</param>
/// <param name="skipByModifiedDate">Does not copy if the destiny file has the same or more recent modified date</param>
public static string FileCopyStream(string sourcePath, string destPath, bool skipHiddenFiles = true, bool skipByModifiedDate = true)
{
    // Buffer should be 64K = 65536‬ bytes 
    // Increasing the buffer size beyond 64k will not help in any circunstance,
    // as the underlying SMB protocol does not support buffer lengths beyond 64k."
    byte[] buffer = new byte[65536];

    if (!File.Exists(sourcePath))
        return $"is not a file: '{sourcePath}'";

    FileInfo sourcefileInfo = new FileInfo(sourcePath);
    FileInfo destFileInfo = null;
    if (File.Exists(destPath))
        destFileInfo = new FileInfo(destPath);

    if (skipHiddenFiles)
    {
        if (sourcefileInfo.Attributes.HasFlag(FileAttributes.Hidden))
            return $"Hidden File Not Copied: '{sourcePath}'";
    }

    using (FileStream input = sourcefileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    using (FileStream output = new FileStream(destPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite, buffer.Length))
    {
        if (skipByModifiedDate && destFileInfo != null)
        {
            if (destFileInfo.LastWriteTime < sourcefileInfo.LastWriteTime)
            {
                input.CopyTo(output, buffer.Length);
                destFileInfo.LastWriteTime = sourcefileInfo.LastWriteTime;
                return $"Replaced: '{sourcePath}'";
            }
            else
            {
                return $"NOT replaced (more recent or same file): '{sourcePath}'";
            }
        }
        else
        {
            input.CopyTo(output, buffer.Length);
            destFileInfo = new FileInfo(destPath);
            destFileInfo.LastWriteTime = sourcefileInfo.LastWriteTime;
            return $"New File: '{sourcePath}'";
        }
    }
}

비동기 API를 사용하는 UWP 및 Winui 3(WindowsAppSdk)의 경우:

public async Task CopyAsync(StorageFolder source, StorageFolder dest)
{
    foreach (var item in await source.GetItemsAsync())

        if (item is StorageFile file)
            await file.CopyAsync(dest);

        else if (item is StorageFolder folder)
            await CopyAsync(folder, await dest.CreateFolderAsync(folder.Name, CreationCollisionOption.OpenIfExists));
}
public static class Extensions
{
    public static void Copy(this DirectoryInfo self, DirectoryInfo destination, bool recursively)
    {
        foreach (var file in self.GetFiles())
        {
            file.CopyTo(Path.Combine(destination.FullName, file.Name));
        }

        if (recursively)
        {
            foreach (var directory in self.GetDirectories())
            {
                directory.Copy(destination.CreateSubdirectory(directory.Name), recursively);
            }
        }
    }
}

사용 예:

var sourceDirectory = new DirectoryInfo(@"C:\source");
var destinationDirectory = new DirectoryInfo(@"C:\destination");

if (destinationDirectory.Exists == false)
{
    sourceDirectory.Copy(destinationDirectory, recursively: true);
}

완전성을 위해 상대 파일 경로 사용 및 선택적으로 파일 교체:

        public static void DuplicateDirectories(
            string sourceDirectory, 
            string targetDirectory,
            string searchPattern = "*.*",
            SearchOption searchOption = SearchOption.AllDirectories)
        {
            foreach (string dir in Directory.GetDirectories(sourceDirectory, searchPattern, searchOption)) 
            {
                var relativePath = Path.GetRelativePath(sourceDirectory, dir);
                var targetPath = Path.Combine(targetDirectory, relativePath);
                Directory.CreateDirectory(targetPath);
            }
        }

        public static void CopyFilesToDirectories(
            string sourceDirectory, 
            string targetDirectory,
            bool replaceIfExists,
            string searchPattern = "*.*",
            SearchOption searchOption = SearchOption.AllDirectories)
        {
            foreach (string filePath in Directory.GetFiles(sourceDirectory, searchPattern, searchOption))
            {
                var relativePath = Path.GetRelativePath(sourceDirectory, filePath);
                var targetPath = Path.Combine(targetDirectory, relativePath);
                File.Copy(filePath, targetPath, replaceIfExists);
            }
        }

그러면:


      var sourceDirectory = @"path\to\source";
      var targetDirectory = @"path\to\target";

       DuplicateDirectories(
            sourceDirectory,
            targetDirectory);

        CopyFilesToDirectories(
            sourceDirectory,
            targetDirectory,
            true);

언급URL : https://stackoverflow.com/questions/58744/copy-the-entire-contents-of-a-directory-in-c-sharp

반응형