Sao chép toàn bộ nội dung của một thư mục trong C #


524

Tôi muốn sao chép toàn bộ nội dung của một thư mục từ vị trí này sang vị trí khác trong C #.

Dường như không có cách nào để làm điều này bằng cách sử dụng System.IOcác lớp mà không có nhiều đệ quy.

Có một phương thức trong VB mà chúng ta có thể sử dụng nếu chúng ta thêm một tham chiếu đến Microsoft.VisualBasic:

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

Đây dường như là một hack khá xấu xí. Có cách nào tốt hơn?


101
Tôi sẽ nói rằng nhìn vào các lựa chọn thay thế được đăng dưới đây, rằng cách VB không trông quá xấu xí.
Kevin Kershaw

41
Làm thế nào nó có thể là một hack khi nó là một phần của .NET Framework? Dừng viết mã và sử dụng những gì bạn có.
AMissico

15
Đó là một quan niệm sai lầm phổ biến. Microsft.VisualBasic chứa tất cả các thủ tục Visual Basic phổ biến giúp mã hóa trong VB dễ dàng hơn nhiều. Microsot.VisualBasic.Compabilities là hội đồng được sử dụng cho di sản VB6.
AMissico

63
Có hơn 2.000 dòng mã cho Microsoft.VisualBasic.Devices.Computer.FileSystem. CopyDirectory đảm bảo bạn không sao chép thư mục mẹ vào thư mục con và các kiểm tra khác. Nó được tối ưu hóa cao, và như vậy. Câu trả lời được chọn là mã mong manh nhất.
AMissico

17
@AMissico - ok, vậy tại sao mã này được tối ưu hóa và đầy đủ trong Microsoft.VisualBasicvà không System.IO? Lý do không có trong Mono là vì tất cả các thư viện được coi là "cốt lõi" là System.[something]- tất cả các thư viện khác đều không có. Tôi không gặp vấn đề gì khi tham khảo thêm một DLL, nhưng có một lý do chính đáng tại sao Microsoft không đưa tính năng này vào System.IO.
Keith

Câu trả lời:


553

Dễ dàng hơn nhiều

//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
Đó thực sự là một đoạn mã đẹp nhưng đây không phải là loại mã có thể được sử dụng ở bất cứ đâu. Các nhà phát triển nên cẩn thận vì dirPath.Replace có thể gây ra hậu quả không mong muốn. Chỉ là một cảnh báo cho những người thích sao chép và dán qua mạng. Mã được đăng bởi @jaysp finance sẽ an toàn hơn vì nó không sử dụng chuỗi. Đặt lại nhưng tôi chắc chắn rằng nó cũng có các trường hợp góc.
Alex

17
Hãy cẩn thận với mã này vì nó sẽ đưa ra một ngoại lệ nếu thư mục đích đã tồn tại. Nó cũng sẽ không ghi đè lên các tập tin đã tồn tại. Chỉ cần thêm một kiểm tra trước khi tạo từng thư mục và sử dụng quá tải File.Copy để ghi đè lên tệp mục tiêu nếu tồn tại.
tham gia

30
@Xaisoft - Replacecó một vấn đề nếu bạn có một mẫu lặp lại bên trong đường dẫn, chẳng hạn "sourceDir/things/sourceDir/things"sẽ trở thành "destinationDir/things/sourceDir/things", nhưng nếu bạn sử dụng thay thế nó sẽ trở thành"destinationDir/things/destinationDir/things"
Keith

35
Tại sao *.*thay vì *? Bạn không muốn sao chép tập tin mà không có phần mở rộng?
Daryl

10
Hãy xây dựng một cái gì đó và đóng góp nó vào .NET Core mã nguồn mở ...: /
Mzn

231

Hmm, tôi nghĩ rằng tôi hiểu sai câu hỏi nhưng tôi sẽ mạo hiểm nó. Có gì sai với phương pháp đơn giản sau đây?

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

EDIT Vì bài đăng này đã thu được số lượng downvote ấn tượng cho một câu trả lời đơn giản như vậy cho một câu hỏi đơn giản không kém, hãy để tôi thêm một lời giải thích. Xin vui lòng đọc này trước khi downvote .

Trước hết, mã này không có ý định thay thế cho mã trong câu hỏi. Nó chỉ nhằm mục đích minh họa.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectorythực hiện một số kiểm tra tính chính xác bổ sung (ví dụ: nguồn và đích có phải là thư mục hợp lệ hay không, nguồn có phải là cha mẹ của mục tiêu không, v.v.) bị thiếu trong câu trả lời này. Mã đó có lẽ cũng được tối ưu hóa hơn.

Điều đó nói rằng, mã hoạt động tốt . Nó đã (gần như giống hệt) được sử dụng trong một phần mềm trưởng thành trong nhiều năm. Ngoài sự hay thay đổi vốn có với tất cả các tay cầm IO (ví dụ: điều gì xảy ra nếu người dùng rút ổ USB theo cách thủ công trong khi mã của bạn đang ghi vào nó?), Không có vấn đề nào được biết đến.

Cụ thể, tôi muốn chỉ ra rằng việc sử dụng đệ quy ở đây hoàn toàn không phải là vấn đề. Về mặt lý thuyết (về mặt khái niệm, đó là giải pháp tao nhã nhất) cũng như trong thực tế: mã này sẽ không tràn vào ngăn xếp . Ngăn xếp đủ lớn để xử lý các phân cấp tệp được lồng sâu. Lâu trước khi không gian ngăn xếp trở thành một vấn đề, giới hạn độ dài đường dẫn thư mục sẽ khởi động.

Lưu ý rằng một người dùng độc hại có thể phá vỡ giả định này bằng cách sử dụng các thư mục được lồng sâu của mỗi chữ cái. Tôi đã không thử điều này. Nhưng chỉ để minh họa điểm: để làm cho mã này tràn vào một máy tính thông thường, các thư mục sẽ phải được lồng vào nhau vài nghìn lần. Đây đơn giản không phải là một kịch bản thực tế.


5
Đây là đệ quy đầu. Nó có thể rơi vào tình trạng tràn ngăn xếp nếu các thư mục được lồng đủ sâu.
spoulson

19
Cho đến gần đây, độ sâu lồng thư mục đã bị HĐH hạn chế. Tôi nghi ngờ rằng bạn sẽ tìm thấy các thư mục được lồng nhiều hơn vài trăm lần (nếu thậm chí). Các mã trên có thể mất nhiều hơn nữa.
Konrad Rudolph

5
Tôi thích cách tiếp cận đệ quy, nguy cơ tràn ngăn xếp là tối thiểu ở mức tồi tệ nhất.
David Basarab

49
@DTashkinov: xin lỗi nhưng điều đó có vẻ hơi quá. Tại sao mã rõ ràng == downvote? Điều ngược lại nên đúng. Phương thức tích hợp sẵn đã được đăng nhưng Keith yêu cầu cụ thể về phương pháp khác . Ngoài ra, bạn có ý gì bởi câu cuối cùng của bạn? Xin lỗi, nhưng tôi không hiểu lý do của bạn để hạ thấp tất cả.
Konrad Rudolph

6
@AMissico: tốt hơn cái gì ? Không ai tuyên bố nó tốt hơn mã VB từ khung. Chúng tôi biết nó không phải là.
Konrad Rudolph

132

Sao chép từ 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
Không có lý do để kiểm tra xem thư mục có tồn tại hay không, chỉ cần gọi Directoty.CreateDirectory sẽ không làm gì nếu thư mục đã tồn tại.
Tal Jerome

1
Đối với những người muốn xử lý các đường dẫn lớn hơn 256 ký tự, bạn có thể sử dụng gói Nuget có tên ZetaLongPaths
AK

2
Câu trả lời này dường như là hữu ích nhất trong tất cả. Bằng cách sử dụng DirectoryInfo thay vì chuỗi, rất nhiều vấn đề tiềm ẩn được tránh.
DaedalusAlpha

50

Thử cái này:

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

Đối số xcopy của bạn có thể thay đổi nhưng bạn có ý tưởng.


3
/ E bảo nó sao chép tất cả các thư mục con (ngay cả những thư mục trống). / Tôi nói với nó rằng nếu đích không tồn tại, hãy tạo một thư mục có tên đó.
d4nt

6
thêm trích dẫn để được an toàn.
jaysonragasa

6
Thêm / Y để tránh bị nhắc ghi đè lên các tệp hiện có. stackoverflow.com/q/191209/138938
Jon Crowell

16
Xin lỗi, nhưng điều này thật kinh khủng. Nó giả định rằng hệ thống đích là cửa sổ. Nó giả định rằng các phiên bản trong tương lai bao gồm xcopy.exe tại đường dẫn cụ thể đó. Nó giả định rằng các tham số của xcopy không thay đổi. Nó đòi hỏi phải lắp ráp các tham số cho xcopy dưới dạng chuỗi, điều này mang đến nhiều tiềm năng lỗi. Ngoài ra, mẫu không đề cập đến bất kỳ xử lý lỗi nào cho kết quả của quá trình bắt đầu, mà tôi mong đợi, bởi vì trái với các phương pháp khác, điều này sẽ thất bại trong âm thầm.
cel sharp

3
@MatthiasJansen, tôi nghĩ bạn đã lấy nó rất cá nhân. Câu trả lời là vấn đề và giải thích nhiều về cách đạt được nó ... Vì câu hỏi không yêu cầu khả năng tương thích đa nền tảng hoặc không sử dụng xcopy hoặc bất cứ điều gì khác mà người đăng chỉ trả lời để giải thích cách này có thể đạt được theo cách đó ... có thể là 1000 cách để làm điều tương tự và câu trả lời khác nhau .. đó là lý do tại sao diễn đàn này ở đây để giải quyết và các lập trình viên trên toàn cầu đến đây để chia sẻ kinh nghiệm của họ. Tôi bình chọn bình luận của bạn.
KMX

47

Hoặc, nếu bạn muốn đi một cách khó khăn, hãy thêm một tham chiếu đến dự án của bạn cho Microsoft.VisualBasic và sau đó sử dụng như sau:

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

Tuy nhiên, sử dụng một trong các hàm đệ quy là cách tốt hơn vì nó sẽ không phải tải dll VB.


1
Điều đó không thực sự khác với cách tôi đã làm - dù sao bạn vẫn cần tải các công cụ tương thích ngược của VB để có thể làm điều đó.
Keith

10
Đang tải lắp ráp VB đắt tiền? Các tùy chọn VB thanh lịch hơn nhiều so với các phiên bản C #.
jwmiller5

3
"Công cụ tương thích ngược của VB" là gì? CopyDirectory sử dụng Shell hoặc Framework.
AMissico

3
Tôi ước nó được bật System.IO.Directory, nhưng tốt hơn là viết lại!
Josh M.

2
Đây là cách để đi imo, dễ dàng hơn nhiều so với bất kỳ tùy chọn nào khác
reggaeg Ức

38

Trang web này luôn giúp tôi rất nhiều, và giờ đến lượt tôi giúp đỡ những người khác với những gì tôi biết.

Tôi hy vọng rằng mã của tôi dưới đây là hữu ích cho một ai đó.

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
Hãy nhớ về dấu gạch chéo ngược
Alexey F

24
Mọi người, sử dụng Path.Combine(). Không bao giờ sử dụng nối chuỗi để đặt đường dẫn tệp với nhau.
Andy

3
Bạn có một OBOB trong đoạn mã trên. Bạn nên sử dụng source_dir.Length + 1, không source_dir.Length.
PellucidWombat

Mã này là một khái niệm tốt, nhưng ... Một tệp không cần phải có "." trong đó, vì vậy sẽ tốt hơn khi sử dụng ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories))
Jean Libera

Cảm ơn bạn @JeanLibera, bạn đã đúng. Tôi đã thay đổi mã với đề nghị của bạn.
jaysp tài trợ

14

Sao chép thư mục đệ quy mà không cần đệ quy để tránh tràn ngăn xếp.

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

mẫu không đệ quy hữu ích :)
Minh Nguyễn

2
Khó có thể tưởng tượng việc thổi bay đống đồ trước khi phát sáng giới hạn đường đi
Ed S.

5

Đây là một lớp tiện ích tôi đã sử dụng cho các nhiệm vụ IO như thế này.

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

Lưu ý rằng Microsoft sử dụng SHFileOperation trong nội bộ cho Microsoft.VisualBasic.
jrh

3

Nó có thể không nhận biết hiệu năng, nhưng tôi đang sử dụng nó cho các thư mục 30 MB và nó hoạt động hoàn hảo. Thêm vào đó, tôi không thích tất cả số lượng mã và đệ quy cần thiết cho một nhiệm vụ dễ dàng như vậy.

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

Lưu ý: ZipFile có sẵn trên .NET 4.5+ trong không gian tên System.IO.Compression


1
Tôi cũng vậy, vì vậy câu hỏi, nhưng câu trả lời được chọn không cần đệ quy. Câu trả lời này tạo ra một tệp zip trên đĩa, rất nhiều công việc bổ sung cho một bản sao tệp - không chỉ bạn đang tạo một bản sao bổ sung của dữ liệu, mà bạn còn dành thời gian để xử lý nén và giải nén nó. Tôi chắc chắn rằng nó hoạt động, giống như cách bạn có thể gõ móng tay vào giày của bạn, nhưng nó hoạt động nhiều hơn với nhiều thứ có thể sai, trong khi có nhiều cách tốt hơn để làm điều đó.
Keith

Lý do tôi kết thúc với điều này là thay thế chuỗi. Như những người khác đã chỉ ra, câu trả lời được chấp nhận thể hiện nhiều mối quan tâm; liên kết nối có thể không hoạt động, cũng như lặp lại mẫu thư mục hoặc tệp mà không có phần mở rộng hoặc tên. Ít mã hơn, ít cơ hội đi sai. Và vì thời gian xử lý không phải là vấn đề đáng lo ngại đối với tôi, nên nó phù hợp với trường hợp cụ thể của tôi
AlexanderD

2
Vâng, đó là giống như lái xe 1000 dặm ra khỏi con đường của bạn để tránh một đèn giao thông duy nhất, nhưng nó là cuộc hành trình của bạn, vì vậy đi cho nó. Kiểm tra các mẫu thư mục là không đáng kể so với những gì ZIP cần làm dưới mui xe. Tôi thực sự khuyên bạn nên chống lại điều này cho bất kỳ ai quan tâm đến việc không lãng phí bộ xử lý, đĩa, điện hoặc nơi cần chạy cùng với các chương trình khác trên cùng một máy. Ngoài ra, nếu bạn đã từng hỏi loại câu hỏi này khi phỏng vấn không bao giờ đi với "mã của tôi đơn giản vì vậy tôi không quan tâm đến thời gian xử lý" - bạn sẽ không nhận được công việc.
Keith

1
Tôi chuyển sang câu trả lời được cung cấp bởi @ justin-r . Tuy nhiên, tôi sẽ để câu trả lời này ở đó như một cách làm khác
AlexanderD

1
Nếu các thư mục nằm trên các chia sẻ mạng riêng biệt và chứa nhiều tệp, đây sẽ là lựa chọn tốt nhất theo quan điểm của tôi.
Daniel Parker

2

Một cải tiến nhỏ về câu trả lời của d4nt, vì bạn có thể muốn kiểm tra lỗi và không phải thay đổi đường dẫn xcopy nếu bạn đang làm việc trên máy chủ và máy phát triển:

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

Đây là mã của tôi hy vọng sự giúp đỡ này

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

Xem câu trả lời đã chọn, bằng cách sử dụng SearchOptioncờ trên các tìm kiếm cho các thư mục và tệp, nó thực hiện điều này trong 4 dòng mã. Ngoài ra kiểm tra .HasFlagphần mở rộng bây giờ trên enums.
Keith

2

Nếu bạn thích câu trả lời phổ biến của Konrad, nhưng bạn muốn sourcenó là một thư mục bên dưới target, thay vì đặt nó dưới dạng targetthư mục, đây là mã cho điều đó. Nó trả về cái vừa tạo DirectoryInfo, rất tiện:

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

Bạn luôn có thể sử dụng cái này , lấy từ trang web của microsofts.

static void Main()
{
    // Copy from the current directory, include subdirectories.
    DirectoryCopy(".", @".\temp", true);
}

private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
{
    // Get the subdirectories for the specified directory.
    DirectoryInfo dir = new DirectoryInfo(sourceDirName);

    if (!dir.Exists)
    {
        throw new DirectoryNotFoundException(
            "Source directory does not exist or could not be found: "
            + sourceDirName);
    }

    DirectoryInfo[] dirs = dir.GetDirectories();
    // If the destination directory doesn't exist, create it.
    if (!Directory.Exists(destDirName))
    {
        Directory.CreateDirectory(destDirName);
    }

    // Get the files in the directory and copy them to the new location.
    FileInfo[] files = dir.GetFiles();
    foreach (FileInfo file in files)
    {
        string temppath = Path.Combine(destDirName, file.Name);
        file.CopyTo(temppath, false);
    }

    // If copying subdirectories, copy them and their contents to new location.
    if (copySubDirs)
    {
        foreach (DirectoryInfo subdir in dirs)
        {
            string temppath = Path.Combine(destDirName, subdir.Name);
            DirectoryCopy(subdir.FullName, temppath, copySubDirs);
        }
    }
}

1
Điều này thật tuyệt - Hãy nhớ file.CopyTo(temppath, false);rằng dòng chữ "sao chép tệp này vào nơi này, chỉ khi nó không tồn tại", mà hầu hết thời gian không phải là những gì chúng ta muốn. Nhưng, tôi có thể hiểu tại sao nó mặc định như vậy. Có thể thêm một cờ vào phương thức cho các tập tin ghi đè.
Andy

2

Phiên bản Proof thay thế của tboswell (có khả năng phục hồi mô hình lặp lại trong 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
Mọi người, sử dụng Path.Combine(). Không bao giờ sử dụng nối chuỗi để đặt đường dẫn tệp với nhau.
Andy

2

Giải pháp của tôi về cơ bản là sửa đổi câu trả lời của @ Termininja, tuy nhiên tôi đã cải thiện nó một chút và nó dường như nhanh hơn gấp 5 lần so với câu trả lời được chấp nhận.

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

EDIT: Sửa đổi @Ahmed Sabry thành foreach song song hoàn toàn sẽ tạo ra kết quả tốt hơn, tuy nhiên mã sử dụng hàm đệ quy và nó không lý tưởng trong một số trường hợp.

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

Xin lỗi vì mã trước đó, nó vẫn có lỗi :( (rơi vào vấn đề súng nhanh nhất). Ở đây, nó đã được thử nghiệm và hoạt động. Chìa khóa là SearchOption.AllDirectories, loại bỏ sự cần thiết phải đệ quy rõ ràng.

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

Đây là một phương thức mở rộng cho DirectoryInfo a la FileInfo.CopyTo (lưu ý overwritetham số):

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

Sử dụng lớp này.

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
Điều này tương tự với các câu trả lời khác, được tái cấu trúc để sử dụng .ToList().ForEach((công việc hơi nhiều, bộ nhớ và chậm hơn một chút so với chỉ liệt kê các thư mục trực tiếp) và như một phương thức mở rộng. Câu trả lời được chọn sử dụng SearchOption.AllDirectoriesvà tránh đệ quy, vì vậy tôi khuyên bạn nên chuyển sang mô hình đó. Ngoài ra, bạn thường không cần tên của các loại trong phương pháp khuyến nông - Tôi muốn đổi tên nó để CopyTo()sao cho nó trở thànhsourceDir.CopyTo(destination);
Keith

1

Một biến thể chỉ có một vòng lặp để sao chép tất cả các thư mục và tệp:

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

Nếu bạn sẽ sử dụng Regex, có lẽ bạn cũng nên Regex.Escape(path)là một phần của thành phần biểu thức (đặc biệt là xem xét trình phân tách đường dẫn Windows). Bạn cũng có thể nhận được lợi ích từ việc tạo (và có thể biên dịch) một new Regex()đối tượng bên ngoài vòng lặp, thay vì dựa vào phương thức tĩnh.
jimbobmcgee

0

Tốt hơn bất kỳ mã nào (phương thức mở rộng cho DirectoryInfo với đệ quy)

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
Tôi không chắc điều này bổ sung gì cho câu trả lời được chấp nhận, ngoài việc sử dụng đệ quy (nơi không cần thiết) và ẩn các ngoại lệ để làm cho việc gỡ lỗi khó hơn.
Keith

0

Sao chép và thay thế tất cả các tập tin của thư mục

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

Chúc mừng cho câu trả lời, nhưng tôi không chắc điều này bổ sung. Ngoài ra try catch throwlà vô nghĩa.
Keith

0

Đoạn mã dưới đây là microsoft gợi ý cách sao chép thư mục và nó được chia sẻ bởi @iato thân yêu, nhưng nó chỉ sao chép các thư mục con và tệp của thư mục nguồn theo cách đệ quykhông sao chép thư mục nguồn tự nó nhấp (như nhấp chuột phải -> sao chép ).

nhưng có một cách khó khăn bên dưới câu trả lời này:

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

nếu bạn muốn sao chép nội dung của thư mục nguồn và các thư mục con theo cách đệ quy, bạn có thể chỉ cần sử dụng nó như thế này:

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

nhưng nếu bạn muốn sao chép thư mục nguồn tự nó (tương tự như bạn đã nhấp chuột phải vào thư mục nguồn và nhấp vào sao chép trong thư mục đích bạn đã nhấp vào dán), bạn nên sử dụng như thế này:

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

đã được đăng một số câu trả lời dưới đây: stackoverflow.com/a/45199038/1951524
Martin Schneider

Cảm ơn @ MA-Maddin, nhưng nó có sao chép chính thư mục nguồn không? hay chỉ là nội dung?
Arash.Zandi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.