Salin seluruh konten direktori di C #

524

Saya ingin menyalin seluruh isi direktori dari satu lokasi ke lokasi lain di C #.

Tampaknya tidak ada cara untuk melakukan ini menggunakan System.IOkelas tanpa banyak rekursi.

Ada metode di VB yang bisa kita gunakan jika kita menambahkan referensi ke Microsoft.VisualBasic:

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

Sepertinya ini hack yang agak jelek. Apakah ada cara yang lebih baik?

Keith
sumber
101
Saya akan mengatakan bahwa melihat alternatif yang diposting di bawah ini, bahwa cara VB tidak terlihat jelek.
Kevin Kershaw
41
Bagaimana ini bisa menjadi retasan ketika itu adalah bagian dari .NET Framework? Berhenti menulis kode dan gunakan apa yang Anda dapatkan.
AMissico
15
Itu adalah kesalahpahaman umum. Microsft.VisualBasic berisi semua prosedur Visual Basic umum yang membuat pengkodean dalam VB jadi lebih mudah. Microsot.VisualBasic.Compatibility adalah perakitan yang digunakan untuk warisan VB6.
AMissico
63
Ada lebih dari 2.000 baris kode untuk Microsoft.VisualBasic.Devices.Computer.FileSystem. CopyDirectory memastikan Anda tidak menyalin folder induk ke folder anak dan cek lainnya. Ini sangat dioptimalkan, dan sebagainya. Jawaban yang dipilih adalah kode rapuh terbaik.
AMissico
17
@ AMissico - ok, jadi mengapa kode ini dioptimalkan dan lengkap Microsoft.VisualBasicdan tidak System.IO? Alasannya tidak ada di Mono adalah karena semua perpustakaan yang dianggap 'inti' adalah System.[something]- semua yang lain tidak. Saya tidak punya masalah merujuk DLL tambahan, tetapi ada alasan bagus mengapa Microsoft belum memasukkan fitur ini di System.IO.
Keith

Jawaban:

553

Jauh lebih mudah

//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);
tboswell
sumber
25
Memang kode yang bagus, tetapi ini bukan jenis kode yang dapat digunakan di mana saja. Pengembang harus berhati-hati karena dirPath.Replace dapat menyebabkan konsekuensi yang tidak diinginkan. Hanya peringatan bagi orang-orang yang suka melakukan salin dan rekatkan melalui internet. Kode yang diposting oleh @jaysponsored lebih aman karena tidak menggunakan string. Ganti tapi saya yakin juga memiliki kasing sudut.
Alex
17
Hati-hati dengan kode ini karena akan mengeluarkan pengecualian jika direktori target sudah ada. Itu juga tidak akan menimpa file yang sudah ada. Cukup tambahkan cek sebelum membuat setiap direktori dan gunakan overload File.Copy untuk menimpa file target jika ada.
joerage
30
@Xaisoft - Replacememiliki masalah jika Anda memiliki pola berulang di dalam jalan, misalnya "sourceDir/things/sourceDir/things"harus menjadi "destinationDir/things/sourceDir/things", tetapi jika Anda menggunakan ganti menjadi"destinationDir/things/destinationDir/things"
Keith
35
Kenapa *.*bukannya *? Apakah Anda tidak ingin menyalin file tanpa ekstensi juga?
Daryl
10
Mari kita membangun sesuatu dan berkontribusi pada Open Source .NET Core ...: /
Mzn
231

Hmm, saya pikir saya salah paham pertanyaannya tapi saya akan mengambil risiko. Apa yang salah dengan metode sederhana berikut ini?

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 Karena posting ini telah mengumpulkan jumlah downvotes yang mengesankan untuk jawaban yang begitu sederhana untuk pertanyaan yang sama-sama sederhana, izinkan saya menambahkan penjelasan. Harap baca ini sebelum downvoting .

Pertama-tama, kode ini tidak dimaksudkan sebagai pengganti untuk kode yang ada dalam pertanyaan. Ini hanya untuk tujuan ilustrasi.

Microsoft.VisualBasic.Devices.Computer.FileSystem.CopyDirectorymelakukan beberapa tes ketepatan tambahan (misalnya apakah sumber dan target adalah direktori yang valid, apakah sumber tersebut adalah induk dari target, dll.) yang hilang dari jawaban ini. Kode itu mungkin juga lebih dioptimalkan.

Konon, kodenya berfungsi dengan baik . Ini telah (hampir identik) telah digunakan dalam perangkat lunak dewasa selama bertahun-tahun. Terlepas dari fickleness inheren yang hadir dengan semua penanganan IO (mis. Apa yang terjadi jika pengguna mencabut USB drive secara manual saat kode Anda menulisnya?), Tidak ada masalah yang diketahui.

Secara khusus, saya ingin menunjukkan bahwa penggunaan rekursi di sini sama sekali bukan masalah. Baik secara teori (secara konseptual, ini adalah solusi paling elegan) maupun dalam praktik: kode ini tidak akan meluap tumpukan . Tumpukannya cukup besar untuk menangani hierarki file yang bahkan sangat bersarang. Jauh sebelum ruang stack menjadi masalah, batasan panjang jalur folder akan muncul.

Perhatikan bahwa pengguna jahat mungkin dapat mematahkan asumsi ini dengan menggunakan direktori yang masing-masing bersarang dalam satu huruf. Saya belum mencoba ini. Tetapi hanya untuk mengilustrasikan intinya: untuk membuat kode ini meluap pada komputer biasa, direktori harus disarangkan beberapa ribu kali. Ini sama sekali bukan skenario yang realistis.

Konrad Rudolph
sumber
5
Ini adalah rekursi kepala. Itu bisa menjadi mangsa stack overflow jika direktori bersarang cukup dalam.
spoulson
19
Sampai baru-baru ini, kedalaman sarang direktori dibatasi oleh OS. Saya ragu Anda akan menemukan direktori yang bersarang lebih dari beberapa ratus kali (jika genap). Kode di atas dapat mengambil lebih banyak.
Konrad Rudolph
5
Saya suka pendekatan rekursif, risiko stack overflow adalah minimum di terburuk.
David Basarab
49
@DTashkinov: baik, permisi, tapi itu agak berlebihan. Mengapa kode jelas == downvote? Yang sebaliknya harus benar. Metode built-in sudah diposting tetapi Keith meminta secara khusus untuk metode lain . Juga, apa yang Anda maksud dengan kalimat terakhir Anda? Maaf, tapi saya sama sekali tidak mengerti alasan Anda untuk menurunkan voting sama sekali.
Konrad Rudolph
6
@ AMissico: lebih baik dari apa ? Tidak ada yang mengklaim itu lebih baik daripada kode VB dari framework. Kami tahu itu bukan.
Konrad Rudolph
132

Disalin dari 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.
}
Justin R.
sumber
8
Tidak ada alasan untuk memeriksa apakah direktori tersebut ada, cukup panggil Directoty.CreateDirectory yang tidak akan melakukan apa-apa jika direktori tersebut sudah ada.
Tal Jerome
1
Bagi mereka yang ingin berurusan dengan jalur yang lebih besar dari 256 karakter, Anda dapat menggunakan paket Nuget yang disebut ZetaLongPaths
AK
2
Jawaban ini tampaknya menjadi yang paling berguna dari semuanya. Dengan menggunakan DirectoryInfo alih-alih string, banyak masalah potensial yang dihindari.
DaedalusAlpha
50

Coba ini:

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

Argumen xcopy Anda mungkin bervariasi tetapi Anda mendapatkan ide.

d4nt
sumber
3
/ E mengatakannya untuk menyalin semua sub direktori (bahkan yang kosong). / Aku bilang kalau tujuan tidak ada buat direktori dengan nama itu.
d4nt
6
tambahkan penawaran ganda agar aman.
jaysonragasa
6
Tambahkan / Y untuk mencegah diminta menimpa file yang ada. stackoverflow.com/q/191209/138938
Jon Crowell
16
Maaf, tapi ini mengerikan. Diasumsikan bahwa sistem target adalah windows. Ini mengasumsikan bahwa versi masa depan termasuk xcopy.exe di jalur spesifik itu. Diasumsikan bahwa parameter xcopy tidak berubah. Perlu untuk merakit parameter untuk xcopy sebagai string, yang memperkenalkan banyak potensi kesalahan. Juga sampel tidak menyebutkan penanganan kesalahan untuk hasil dari proses yang dimulai, yang saya harapkan, karena bertentangan dengan metode lain ini akan gagal diam-diam.
cel tajam
3
@MatthiasJansen, saya pikir Anda menganggapnya sangat pribadi. Jawabannya to the point dan menjelaskan banyak tentang bagaimana mencapainya ... Karena pertanyaannya tidak menuntut kompatibilitas lintas platform atau tidak menggunakan xcopy atau apa pun poster hanya menjawab untuk menjelaskan bagaimana ini dapat dicapai satu arah ... Ada mungkin ada 1000 cara untuk melakukan hal yang sama dan jawabannya bervariasi .. itu sebabnya forum ini ada di sini untuk membahas dan programmer di seluruh dunia datang ke sini untuk berbagi pengalaman mereka. Saya memilih komentar Anda.
KMX
47

Atau, jika Anda ingin menempuh jalan yang sulit, tambahkan referensi ke proyek Anda untuk Microsoft.VisualBasic dan kemudian gunakan yang berikut ini:

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

Namun, menggunakan salah satu fungsi rekursif adalah cara yang lebih baik karena tidak perlu memuat dll VB.

Josef
sumber
1
Itu tidak benar-benar berbeda dari bagaimana saya melakukannya - Anda masih perlu memuat hal kompatibilitas-belakang VB agar dapat melakukannya.
Keith
10
Apakah memuat perakitan VB mahal? Opsi VB jauh lebih elegan daripada versi C #.
jwmiller5
3
Apa "hal kompatibilitas mundur VB"? CopyDirectory menggunakan Shell atau Framework.
AMissico
3
Saya berharap itu aktif System.IO.Directory, tetapi lebih baik daripada menulis ulang!
Josh M.
2
Ini adalah cara untuk pergi ke imo, jauh lebih mudah daripada opsi lain mana pun
reggaeguitar
38

Situs ini selalu banyak membantu saya, dan sekarang giliran saya untuk membantu orang lain dengan apa yang saya ketahui.

Saya harap kode saya di bawah ini bermanfaat bagi seseorang.

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)));
    }
jaysponsored
sumber
1
Ingat tentang trailing backslash
Alexey F
24
Teman-teman, gunakan Path.Combine(). Jangan pernah menggunakan penggabungan string untuk menyatukan jalur file.
Andy
3
Anda memiliki OBOB dalam cuplikan kode di atas. Anda seharusnya menggunakan source_dir.Length + 1, bukan source_dir.Length.
PellucidWombat
Kode ini adalah konsep yang baik, tetapi ... File tidak harus memiliki "." di dalamnya, jadi akan lebih baik menggunakan ystem.IO.Directory.GetFiles (source_dir, "*", System.IO.SearchOption.AllDirectories))
Jean Libera
@JeanLibera terima kasih, kamu benar. Saya mengubah kode dengan saran Anda.
jaysponsored
14

Salin folder secara rekursif tanpa rekursi untuk menghindari stack overflow.

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;
    }
}
Jens Granlund
sumber
templat non-rekursi yang berguna :)
Minh Nguyen
2
Sulit membayangkan meniup tumpukan sebelum menyilaukan batas jalur
Ed S.
5

Inilah kelas utilitas yang saya gunakan untuk tugas-tugas IO seperti ini.

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

sumber
Perhatikan bahwa Microsoft menggunakan SHFileOperation secara internal untuk Microsoft.VisualBasic.
jrh
3

Mungkin tidak menyadari kinerja, tapi saya menggunakannya untuk folder 30MB dan berfungsi dengan sempurna. Plus, saya tidak suka semua jumlah kode dan rekursi yang diperlukan untuk tugas yang mudah.

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

Catatan: ZipFile tersedia di .NET 4.5+ di System.IO.Compression namespace

AlexanderD
sumber
1
Saya juga tidak, maka pertanyaannya, tetapi jawaban yang dipilih tidak perlu rekursi. Jawaban ini membuat file zip pada disk, yang merupakan banyak pekerjaan tambahan untuk salinan file - tidak hanya Anda membuat salinan data tambahan, tetapi Anda menghabiskan waktu prosesor mengompres dan mendekompresinya. Saya yakin itu berhasil, dengan cara yang sama Anda mungkin bisa mengetuk paku dengan sepatu Anda, tetapi lebih banyak bekerja dengan lebih banyak hal yang bisa salah, sementara ada cara yang lebih baik untuk melakukannya.
Keith
Alasan saya berakhir dengan ini adalah penggantian string. Seperti yang telah ditunjukkan orang lain, jawaban yang diterima menghadirkan banyak keprihatinan; tautan sambungan mungkin tidak berfungsi, serta mengulangi pola atau file folder tanpa ekstensi atau nama. Lebih sedikit kode, lebih sedikit kesempatan untuk salah. Dan karena waktu prosesor bukan masalah bagi saya, itu membuatnya cocok untuk kasus khusus saya
AlexanderD
2
Ya, itu seperti mengemudi 1000 mil dari jalan Anda untuk menghindari lampu lalu lintas tunggal, tapi ini perjalanan Anda, jadi lakukanlah. Memeriksa pola folder sepele dibandingkan dengan apa yang perlu dilakukan ZIP di bawah tenda. Saya sangat merekomendasikan ini untuk siapa pun yang peduli untuk tidak menyia-nyiakan prosesor, disk, listrik atau di mana ini perlu dijalankan bersama program lain pada mesin yang sama. Juga, jika Anda pernah ditanya jenis pertanyaan ini saat wawancara tidak pernah pergi dengan "kode saya sederhana jadi saya tidak peduli tentang waktu prosesor" - Anda tidak akan mendapatkan pekerjaan.
Keith
1
Saya beralih ke jawaban yang diberikan oleh @ justin-r . Namun, saya akan meninggalkan jawaban ini di sana sebagai cara lain untuk melakukannya
AlexanderD
1
Jika folder berada di jaringan yang terpisah dan berisi banyak file, ini akan menjadi pilihan terbaik menurut saya.
Danny Parker
2

Peningkatan kecil pada jawaban d4nt, karena Anda mungkin ingin memeriksa kesalahan dan tidak perlu mengubah jalur xcopy jika Anda bekerja pada server dan mesin pengembangan:

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));
    }
}
Chris S
sumber
2

Ini kode saya berharap bantuan ini

    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;
    }
Khoi_Vjz_Boy
sumber
Lihat jawaban yang dipilih, dengan menggunakan SearchOptiontanda pada pencarian folder dan file yang dilakukan dalam 4 baris kode. Periksa juga .HasFlagekstensi sekarang di enum.
Keith
2

Jika Anda menyukai jawaban populer Konrad, tetapi Anda ingin sourceitu sendiri menjadi folder di bawah target, daripada meletakkan anak-anak itu di bawah targetfolder, inilah kode untuk itu. Ini mengembalikan yang baru dibuat DirectoryInfo, yang berguna:

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;
}
toddmo
sumber
2

Anda selalu dapat menggunakan ini , diambil dari situs web 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);
        }
    }
}
iato
sumber
1
Ini bagus - Ingatlah baris file.CopyTo(temppath, false);mengatakan "salin file ini ke tempat ini, hanya jika itu tidak ada", yang sebagian besar waktu tidak seperti yang kita inginkan. Tapi, saya bisa mengerti mengapa itu default untuk itu. Mungkin menambahkan tanda pada metode untuk menimpa file.
Andy
2

tboswell mengganti versi Proof (yang tahan terhadap pola berulang dalam 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);
    }
bh_earth0
sumber
3
Teman-teman, gunakan Path.Combine(). Jangan pernah menggunakan penggabungan string untuk menyatukan jalur file.
Andy
2

Solusi saya pada dasarnya adalah modifikasi dari jawaban @ Termininja, namun saya telah meningkatkannya sedikit dan tampaknya lebih dari 5 kali lebih cepat daripada jawaban yang diterima.

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: Memodifikasi @Ahmed Sabry ke full parallel foreach memang menghasilkan hasil yang lebih baik, namun kode menggunakan fungsi rekursif dan tidak ideal dalam beberapa situasi.

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));
}
Jerry Liang
sumber
1

Maaf untuk kode sebelumnya, masih ada bug :( (menjadi mangsa masalah senjata tercepat). Di sini diuji dan berfungsi. Kuncinya adalah SearchOption.AllDirectories, yang menghilangkan kebutuhan rekursi eksplisit.

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);
    }
}
Vinko Vrsalovic
sumber
1

Berikut adalah metode ekstensi untuk DirectoryInfo a la FileInfo.CopyTo (perhatikan overwriteparameternya):

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;
}
Daryl
sumber
1

Gunakan kelas ini.

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);
    }
}
Ahmed Sabry
sumber
1
Ini mirip dengan jawaban lain, refactored untuk digunakan .ToList().ForEach((yang sedikit lebih banyak pekerjaan, memori dan sedikit lebih lambat daripada hanya menyebutkan direktori secara langsung) dan sebagai metode ekstensi. Jawaban yang dipilih menggunakan SearchOption.AllDirectoriesdan menghindari rekursi, jadi saya akan merekomendasikan beralih ke model itu. Juga, Anda biasanya tidak memerlukan nama tipe dalam metode ekstensi - Saya akan mengubah nama CopyTo()itu menjadisourceDir.CopyTo(destination);
Keith
1

Satu varian dengan hanya satu loop untuk menyalin semua folder dan file:

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);
}
Termininja
sumber
Jika Anda akan menggunakan Regex, Anda mungkin juga harus Regex.Escape(path)sebagai bagian dari komposisi ekspresi Anda (terutama mengingat pemisah jalur Windows). Anda mungkin juga mendapatkan manfaat dari membuat (dan mungkin mengkompilasi) new Regex()objek di luar loop, daripada mengandalkan metode statis.
jimbobmcgee
0

Lebih baik daripada kode apa pun (metode ekstensi ke DirectoryInfo dengan rekursi)

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;
        }
    }
malballah
sumber
1
Saya tidak yakin apa ini menambahkan jawaban yang diterima, selain menggunakan rekursi (di mana itu tidak perlu) dan menyembunyikan pengecualian untuk membuat debugging lebih sulit.
Keith
0

Salin dan ganti semua file folder

        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);
            }
    }
Lakmal
sumber
Bersorak untuk jawabannya, tapi saya tidak yakin apa yang ini tambahkan Juga try catch throwtidak ada gunanya.
Keith
0

Kode di bawah ini adalah saran microsoft bagaimana cara menyalin direktori dan dibagikan oleh dear @iato tetapi hanya menyalin sub direktori dan file folder sumber secara rekursif dan tidak menyalin folder sumber itu sendiri (seperti klik kanan -> salin ).

tetapi ada cara rumit di bawah jawaban ini:

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

jika Anda ingin menyalin isi dari sumber folder dan subfolder rekursif Anda hanya dapat menggunakannya seperti ini:

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

tetapi jika Anda ingin menyalin direktori sumber itu sendiri (mirip dengan yang Anda klik kanan pada folder sumber dan klik salin kemudian di folder tujuan yang Anda klik tempelkan) Anda harus menggunakan seperti ini:

 string source = @"J:\source\";
 string dest= @"J:\destination\";
 DirectoryCopy(source, Path.Combine(dest, new DirectoryInfo(source).Name));
Arash.Zandi
sumber
telah diposting beberapa jawaban di bawah ini: stackoverflow.com/a/45199038/1951524
Martin Schneider
Terima kasih @ MA-Maddin, tetapi apakah itu menyalin folder sumber itu sendiri? atau hanya isinya?
Arash.Zandi