คัดลอกเนื้อหาทั้งหมดของไดเรกทอรีใน 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 มีขั้นตอนทั่วไปของ Visual Basic ทั้งหมดที่ทำให้การเข้ารหัสใน VB ง่ายขึ้นมาก Microsot.VisualBasic.Compatibility เป็นชุดประกอบที่ใช้สำหรับ VB6 ดั้งเดิม
— AMissico

63
มีรหัสมากกว่า 2,000 บรรทัดสำหรับ Microsoft.VisualBasic.Devices.Computer.FileSystem 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 การเปลี่ยนอาจทำให้เกิดผลที่ไม่พึงประสงค์ เพียงแค่เตือนให้คนที่ชอบทำสำเนาและวางบนเน็ต รหัสที่โพสต์โดย @jaysponsored ปลอดภัยกว่าเพราะไม่ได้ใช้สตริงแทนที่ แต่ฉันแน่ใจว่ามันมีตัวเรือนมุม
— อเล็กซ์

17
ระวังด้วยรหัสนี้เพราะจะทำให้เกิดข้อยกเว้นหากไดเรกทอรีเป้าหมายนั้นมีอยู่แล้ว มันจะไม่เขียนทับไฟล์ที่มีอยู่แล้ว เพียงเพิ่มการตรวจสอบก่อนสร้างแต่ละไดเรกทอรีและใช้เกินพิกัดของ File.Copy เพื่อเขียนทับไฟล์เป้าหมายหากมีอยู่
— joerage

30
@ Xaisoft - Replaceมีปัญหาหากคุณมีรูปแบบการทำซ้ำภายในเส้นทางตัวอย่างเช่น"sourceDir/things/sourceDir/things"ควรจะกลายเป็น"destinationDir/things/sourceDir/things"แต่ถ้าคุณใช้แทนที่มันจะกลายเป็น"destinationDir/things/destinationDir/things"
— Keith

35
ทำไม*.*แทน*? คุณไม่ต้องการคัดลอกไฟล์โดยไม่มีนามสกุลหรือไม่
— Daryl

10
มาสร้างบางอย่างและมีส่วนร่วมกับ Open Source .NET Core ... : /
— Mzn

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

แก้ไขเนื่องจากการโพสต์นี้ได้รวบรวมจำนวน downvotes ที่น่าประทับใจสำหรับคำตอบง่ายๆสำหรับคำถามง่าย ๆ ให้ฉันเพิ่มคำอธิบาย โปรด อ่านก่อนลงคะแนน

ก่อนอื่นรหัสนี้ไม่ได้มีเจตนาเพื่อเป็นการแทนที่ในรหัสในคำถาม มันมีวัตถุประสงค์เพื่อประกอบการอธิบายเท่านั้น

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectoryทำการทดสอบความถูกต้องเพิ่มเติมบางอย่าง (เช่นแหล่งที่มาและเป้าหมายเป็นไดเรกทอรีที่ถูกต้องไม่ว่าจะเป็นแหล่งกำเนิดของเป้าหมาย ฯลฯ ) ที่หายไปจากคำตอบนี้ รหัสนั้นน่าจะเหมาะกว่า

ที่กล่าวว่ารหัสทำงานได้ดี มันมี (เกือบจะเหมือนกัน) ถูกนำมาใช้ในซอฟแวร์สำหรับผู้ใหญ่ปี นอกเหนือจากความไม่แน่นอนที่มีอยู่ในตัวจัดการ IO ทั้งหมด (เช่นจะเกิดอะไรขึ้นถ้าผู้ใช้ถอดปลั๊ก USB ไดรฟ์ด้วยตนเองในขณะที่โค้ดของคุณเขียนอยู่?) ไม่มีปัญหาใด ๆ

โดยเฉพาะอย่างยิ่งฉันต้องการชี้ให้เห็นว่าการใช้การเรียกซ้ำที่นี่ไม่ใช่ปัญหาอย่างแน่นอน ทั้งในทางทฤษฎี (แนวคิดมันเป็นทางออกที่สวยงามมากที่สุด) หรือในการปฏิบัติ: รหัสนี้จะไม่ล้นสแต็ค สแต็กมีขนาดใหญ่พอที่จะจัดการกับลำดับชั้นของไฟล์ที่ซ้อนกันได้ นานก่อนที่พื้นที่สแต็กจะกลายเป็นปัญหาข้อ จำกัด ความยาวพา ธ ของโฟลเดอร์จะเริ่มขึ้น

ขอให้สังเกตว่าผู้ใช้ที่เป็นอันตรายอาจสามารถทำลายสมมติฐานนี้ได้โดยใช้ไดเรกทอรีที่ซ้อนกันของตัวอักษรละตัว ฉันไม่ได้ลองสิ่งนี้ แต่เพียงเพื่อแสดงจุด: เพื่อให้รหัสนี้ล้นบนคอมพิวเตอร์ทั่วไปไดเรกทอรีจะต้องซ้อนกันสองสามพันครั้ง นี่ไม่ใช่สถานการณ์จริง


5
นี่คือการเรียกซ้ำหัวหน้า มันสามารถตกเป็นเหยื่อของการล้นสแต็กหากไดเรกทอรีที่ซ้อนกันลึกพอ
— spoulson

19
จนกระทั่งเมื่อเร็ว ๆ นี้ความลึกในการซ้อนของไดเรกทอรีถูก จำกัด โดยระบบปฏิบัติการ ฉันสงสัยว่าคุณจะพบไดเรกทอรีที่ซ้อนกันมากกว่าสองสามร้อยครั้ง (ถ้ามี) รหัสข้างต้นอาจใช้เวลามากขึ้น
— Konrad Rudolph

5
ฉันชอบวิธีแบบเรียกซ้ำความเสี่ยงของการล้นสแต็กนั้นน้อยที่สุด
— David Basarab

49
@Tashkinov: ขอโทษด้วย แต่ดูเหมือนว่าตาดมากเกินไป ทำไมรหัสที่ชัดเจน == downvote สิ่งที่ตรงกันข้ามควรเป็นจริง วิธีการในตัวได้รับการโพสต์แล้ว แต่ Keith ขอวิธีการอื่นโดยเฉพาะ นอกจากนี้คุณหมายถึงอะไรโดยประโยคสุดท้ายของคุณ? ขออภัย แต่ฉันไม่เข้าใจเหตุผลที่คุณลงคะแนนเลย
— 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 ซึ่งจะไม่ทำอะไรเลยถ้ามีไดเรกทอรีอยู่แล้ว
— Tal เจอโรม

1
สำหรับผู้ที่ต้องการจัดการกับเส้นทางที่มีความยาวมากกว่า 256 ตัวอักษรคุณสามารถใช้แพ็คเกจ Nuget ชื่อ 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 บอกให้คัดลอกไดเรกทอรีย่อยทั้งหมด (แม้แต่รายการที่ว่างเปล่า) / ฉันบอกว่าถ้าปลายทางไม่มีอยู่ให้สร้างไดเรกทอรีด้วยชื่อนั้น
— d4nt

6
เพิ่มเครื่องหมายคำพูดคู่เพื่อความปลอดภัย
— jaysonragasa

6
เพิ่ม / Y เพื่อป้องกันการได้รับแจ้งให้เขียนทับไฟล์ที่มีอยู่ stackoverflow.com/q/191209/138938
— Jon Crowell

16
ขออภัย แต่มันแย่มาก มันถือว่าระบบเป้าหมายเป็น windows สันนิษฐานว่ารุ่นในอนาคตรวม xcopy.exe ที่เส้นทางที่ระบุ สันนิษฐานว่าพารามิเตอร์ของ xcopy ไม่เปลี่ยนแปลง มันต้องการรวบรวมพารามิเตอร์สำหรับ xcopy เป็นสตริงซึ่งแนะนำข้อผิดพลาดมากมายที่อาจเกิดขึ้น นอกจากนี้ตัวอย่างไม่ได้พูดถึงการจัดการข้อผิดพลาดใด ๆ สำหรับผลลัพธ์ของกระบวนการเริ่มต้นซึ่งฉันคาดหวังเพราะตรงกันข้ามกับวิธีการอื่น ๆ นี้จะล้มเหลวอย่างเงียบ ๆ
— cel sharp

3
@ MatiasJansen ฉันคิดว่าคุณเป็นคนที่มีความเป็นส่วนตัว คำตอบคือตรงประเด็นและอธิบายมากเกี่ยวกับวิธีการบรรลุ ... เนื่องจากคำถามไม่ต้องการความเข้ากันได้ข้ามแพลตฟอร์มหรือไม่ใช้ xcopy หรือสิ่งอื่นโปสเตอร์เพียงตอบเพื่ออธิบายวิธีนี้สามารถทำได้ทางเดียว ... มี อาจเป็น 1,000 วิธีในการทำสิ่งเดียวกันและคำตอบต่างกันไป .. นั่นเป็นสาเหตุที่ฟอรัมนี้อยู่ที่นี่เพื่อพูดคุยและโปรแกรมเมอร์ทั่วโลกมาที่นี่เพื่อแบ่งปันประสบการณ์ของพวกเขา ฉันลงคะแนนความคิดเห็นของคุณ
— KMX

47

หรือถ้าคุณต้องการใช้วิธีที่ยากเพิ่มการอ้างอิงถึงโครงการของคุณสำหรับ Microsoft.VisualBasic แล้วใช้สิ่งต่อไปนี้:

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

อย่างไรก็ตามการใช้หนึ่งในฟังก์ชั่นวนซ้ำเป็นวิธีที่ดีกว่าในการดำเนินการเนื่องจากไม่จำเป็นต้องโหลด VB dll


1
นั่นไม่แตกต่างจากวิธีที่ฉันทำอยู่ดี - คุณยังต้องโหลดข้อมูลความเข้ากันได้แบบย้อนกลับของ VB เพื่อให้สามารถทำสิ่งนั้นได้
— Keith

10
การโหลดชุดประกอบ VB มีราคาแพงหรือไม่ ตัวเลือก VB นั้นสวยงามกว่ารุ่น C # มาก
— jwmiller5

3
อะไร "สิ่งที่เข้ากันได้ย้อนหลังของ VB"? CopyDirectory ใช้ทั้ง Shell หรือ Framework
— AMissico

3
ฉันหวังว่ามันจะเปิดอยู่System.IO.Directoryแต่จะดีกว่าเขียนใหม่!
— Josh M.

2
นี่เป็นวิธีที่จะไป imo ง่ายกว่าตัวเลือกอื่น ๆ
— 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()คนใช้ อย่าใช้การต่อสตริงเพื่อรวมพา ธ ของไฟล์เข้าด้วยกัน
— แอนดี้

3
คุณมี OBOB ในตัวอย่างโค้ดข้างต้น คุณควรจะใช้ไม่ได้source_dir.Length + 1 source_dir.Length
— PellucidWombat

รหัสนี้เป็นแนวคิดที่ดี แต่ ... ไฟล์ไม่จำเป็นต้องมี "." ในนั้นดังนั้นจะเป็นการดีกว่าที่จะใช้ ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories))
— Jean Libera

ขอบคุณ @JeanLibera คุณพูดถูก ฉันเปลี่ยนรหัสด้วยคำแนะนำของคุณ
— jaysponsored

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 ใช้ SHFileOperation เป็นการภายในสำหรับ Microsoft.VisualBasic
— 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 พร้อมใช้งานบน. NET 4.5+ ในเนมสเปซ System.IO.Compression


1
ฉันไม่ได้เป็นเช่นนั้นดังนั้นคำถาม แต่คำตอบที่เลือกไม่จำเป็นต้องเรียกซ้ำ คำตอบนี้สร้างไฟล์ zip บนดิสก์ซึ่งเป็นงานเพิ่มเติมจำนวนมากสำหรับการคัดลอกไฟล์ไม่เพียง แต่คุณกำลังสร้างสำเนาข้อมูลเพิ่มเติม แต่คุณใช้เวลาประมวลผลในการบีบอัดและคลายการบีบอัดข้อมูล ฉันแน่ใจว่ามันใช้งานได้เช่นเดียวกับที่คุณสามารถใช้ตะปูกับรองเท้าของคุณได้ แต่มันจะทำงานได้ดีกับสิ่งต่าง ๆ ที่ผิดพลาดมากขึ้นในขณะที่มีวิธีที่ดีกว่าในการทำมัน
— Keith

เหตุผลที่ฉันลงเอยด้วยสิ่งนี้คือการแทนที่สตริง ตามที่คนอื่น ๆ ได้ชี้ให้เห็นคำตอบที่ยอมรับนำเสนอความกังวลมากมาย; ลิงก์เชื่อมต่ออาจใช้งานไม่ได้รวมถึงรูปแบบโฟลเดอร์หรือไฟล์ซ้ำโดยไม่มีนามสกุลหรือชื่อ รหัสน้อยโอกาสน้อยที่จะผิดพลาด และเนื่องจากเวลาตัวประมวลผลไม่ได้เป็นปัญหาสำหรับฉันมันจึงเหมาะสำหรับกรณีเฉพาะของฉัน
— AlexanderD

2
ใช่นั่นเหมือนกับการขับรถออกไป 1,000 ไมล์เพื่อหลีกเลี่ยงสัญญาณไฟจราจรเดียว แต่เป็นการเดินทางของคุณดังนั้นไปเลย การตรวจสอบรูปแบบโฟลเดอร์นั้นเล็กน้อยเมื่อเทียบกับสิ่งที่ ZIP ต้องทำภายใต้ประทุน ฉันขอแนะนำอย่างยิ่งให้กับผู้ที่ไม่ใส่ใจกับการไม่สูญเสียตัวประมวลผลดิสก์ไฟฟ้าหรือที่ที่ต้องทำงานควบคู่ไปกับโปรแกรมอื่น ๆ ในเครื่องเดียวกัน นอกจากนี้หากคุณเคยถามคำถามประเภทนี้ในการสัมภาษณ์อย่าไปกับ "รหัสของฉันนั้นง่ายดังนั้นฉันไม่สนใจเวลาประมวลผล" - คุณจะไม่ได้งาน
— Keith

1
ฉันเปลี่ยนไปคำตอบให้โดย @ justin-R ถึงกระนั้นฉันจะทิ้งคำตอบนี้ไว้ที่นั่นเช่นเดียวกับวิธีอื่นในการทำเช่นนี้
— AlexanderD

1
หากโฟลเดอร์อยู่ในเครือข่ายที่แยกจากกันและมีไฟล์จำนวนมากนี่จะเป็นตัวเลือกที่ดีที่สุดในความคิดของฉัน
— Danny Parker

2

การปรับปรุงเล็กน้อยในคำตอบของ d4nt เนื่องจากคุณอาจต้องการตรวจสอบข้อผิดพลาดและไม่ต้องเปลี่ยนเส้นทาง xcopy หากคุณทำงานบนเซิร์ฟเวอร์และเครื่องพัฒนา:

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ส่วนขยายทันทีด้วย enums
— Keith

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);ว่าบรรทัดบอกว่า "คัดลอกไฟล์นี้ไปยังสถานที่นี้เฉพาะในกรณีที่ไม่มี" ซึ่งส่วนใหญ่แล้วไม่ใช่สิ่งที่เราต้องการ แต่ฉันเข้าใจได้ว่าทำไมมันถึงเป็นค่าเริ่มต้น อาจเพิ่มการตั้งค่าสถานะให้กับวิธีการเขียนทับไฟล์
— แอนดี้

2

tboswell 's แทนที่รุ่น Proof (ซึ่งยืดหยุ่นต่อรูปแบบการทำซ้ำใน filepath)

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()คนใช้ อย่าใช้การต่อสตริงเพื่อรวมพา ธ ของไฟล์เข้าด้วยกัน
— แอนดี้

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 เป็น full 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

ขออภัยสำหรับรหัสก่อนหน้านี้ยังคงมีข้อบกพร่อง :( (ตกเป็นเหยื่อของปัญหาปืนที่เร็วที่สุด) ที่นี่มีการทดสอบและใช้งานได้ที่สำคัญคือ SearchOption.AllDirectories ซึ่งไม่จำเป็นต้องเรียกซ้ำอย่างชัดเจน

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

นี่คือวิธีการขยายสำหรับ DirectoryInfo a la 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
นี้คล้ายกับคำตอบอื่น ๆ refactored ที่จะใช้.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);
}

หากคุณกำลังจะใช้Regexคุณก็ควรRegex.Escape(path)เป็นส่วนหนึ่งขององค์ประกอบการแสดงออกของคุณด้วย (โดยเฉพาะอย่างยิ่งเมื่อพิจารณาตัวคั่นพา ธ Windows) คุณอาจได้รับประโยชน์จากการสร้าง (และอาจจะรวบรวม) new Regex()วัตถุนอกลูปแทนที่จะอาศัยวิธีคงที่
— jimbobmcgee

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

รหัสด้านล่างคือข้อเสนอแนะของ Microsoft วิธีคัดลอกไดเรกทอรี และใช้ร่วมกันโดย 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));

ได้รับการโพสต์แล้วบางคำตอบด้านล่าง: stackoverflow.com/a/45199038/1951524
— Martin Schneider

ขอบคุณ @ MA-Maddin แต่คัดลอกโฟลเดอร์ซอร์สเองหรือไม่ หรือเพียงแค่เนื้อหา
— Arash.Zandi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.