คัดลอกเนื้อหาทั้งหมดของไดเรกทอรีใน 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.