Click here to Skip to main content
12,396,054 members (64,547 online)
Click here to Skip to main content
Add your own
alternative version

Stats

338.7K views
4.8K downloads
186 bookmarked
Posted

xDirectory.Copy() - Copy Folder, SubFolders, and Files

, 19 Oct 2006 CPOL
Rate this:
Please Sign up or sign in to vote.
The .NET framework doesn't have a Directory.Copy() method. So I developed one myself.

Introduction

I was near the end of a drawn out development project for adding users to an Active Directory server when I was informed I would also need to set up their Thunderbird client settings. This required me to create a copy of a directory, sub-directories, and files over at the user's "My Documents" folder.

I tried using Microsoft's MSDN Online to search for a way to copy directories with their subfolders and files, when I determined that the Directory.Copy() method did not exist natively. I did not find anything remotely close.

I then searched the internet for other user's code examples, hoping someone had found a solution to this seemingly simple problem. And I did come across a few; still, the code I did find was inelegant and very much a "quick fix".

So I ended up writing my own.

NEW! Version 3.0 Description

Written October 18th, 2006

Well, after much more feedback and a lot of research, I give you Version 3 of the xDirectory Copier. This version is built on an almost completely different method: It's event-driven.

Yes, that's right, the xDirectory object now copies files and subfolders on a separate thread, and responds with events. This version will work both with Console as well as GUI (such as Windows Forms-Based) programs. It is UI-Thread Friendly, so it will allow you to update your controls on the events. It can give you a progress report, and you can cancel the copy thread at any time. Also, you can set multiple file filters to copy multiple file types (see below).

You'll definately want to download the provided code and look through it yourself. There's enough changes in the code that posting it here is probably just wasting your time. I will however post some highlights.

So, to use this new class, you would do this:

xDirectory Copier = new xDirectory();
Copier.ItemIndexed += new ItemIndexedEventHandler(Copier_ItemIndexed);
Copier.IndexComplete += new IndexCompleteEventHandler(Copier_IndexComplete);
Copier.ItemCopied += new ItemCopiedEventHandler(Copier_ItemCopied);
Copier.CopyComplete += new CopyCompleteEventHandler(Copier_CopyComplete);
Copier.CopyError += new CopyErrorEventHandler(Copier_CopyError);

Then, there are two ways to use the code once initialized. You can do as below:

Copier.Source = new DirectoryInfo(sSource);
Copier.Destination = new DirectoryInfo(sDestination);
Copier.Overwrite = true;
Copier.FolderFilter = "*";
Copier.FileFilters.Add("*.jpg");
Copier.FileFilters.Add("*.bmp");
Copier.FileFilters.Add("*.png");
Copier.StartCopy();

Or, you can simply call the overloaded StartCopy Method:

List<string> FileFilters = new List<string>();
FileFilters.Add("*.jpg");
Copier.StartCopy(sSource, sDestination, FileFilters, null, true);

If you place the Initialization part in a Form_Load method, and then put Usage part in a Button_Click event, it will run asynchronously and will allow you to update your controls (such as a progress bar) through the event methods.

One of the best parts of this new class is that if there's an error in copying a file, instead of cancelling the entire process, it will simply send a 'CopyError' Event! So you can list the files that had errors in copying but continue to copy the rest of the files successfully!

And here's the method that does all the work. It's a bit more complex-looking than version 2:

/// <span class="code-SummaryComment"><summary></span>
/// The Main Work Method of the xDirectory Class: Handled in a Separate Thread.
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="StateInfo">Undefined</param></span>
private void DoWork(object StateInfo)
{
    _CopierStatus = xDirectoryStatus.Started;

    int iterator = 0;
    List<DirectoryInfo> FolderSourceList = new List<DirectoryInfo>();
    List<FileInfo> FileSourceList = new List<FileInfo>();
    DirectoryInfo FolderPath;
    FileInfo FilePath;

    try
    {
        // Part 1: Indexing
        ///////////////////////////////////////////////////////
        
        _CopierStatus = xDirectoryStatus.Indexing;
        
        FolderSourceList.Add(_Source);

        while (iterator < FolderSourceList.Count)
        {
            if (_CancelCopy) return;

            foreach (DirectoryInfo di in 
                     FolderSourceList[iterator].GetDirectories(_FolderFilter))
            {
                if (_CancelCopy) return;

                FolderSourceList.Add(di);

                OnItemIndexed(new ItemIndexedEventArgs(
                    di.FullName,
                    0,
                    FolderSourceList.Count,
                    true));
            }

            foreach (string FileFilter in _FileFilters)
            {
                foreach (FileInfo fi in 
                         FolderSourceList[iterator].GetFiles(FileFilter))
                {
                    if (_CancelCopy) return;

                    FileSourceList.Add(fi);

                    OnItemIndexed(new ItemIndexedEventArgs(
                        fi.FullName,
                        fi.Length,
                        FileSourceList.Count,
                        false));
                }
            }

            iterator++;
        }

        OnIndexComplete(new IndexCompleteEventArgs(
            FolderSourceList.Count, 
            FileSourceList.Count));



        // Part 2: Destination Folder Creation
        ///////////////////////////////////////////////////////

        _CopierStatus = xDirectoryStatus.CopyingFolders;

        for (iterator = 0; iterator < FolderSourceList.Count; iterator++)
        {
            if (_CancelCopy) return;

            
            if (FolderSourceList[iterator].Exists)
            {
                FolderPath = new DirectoryInfo(
                    _Destination.FullName +
                    Path.DirectorySeparatorChar +
                    FolderSourceList[iterator].FullName.Remove(0, 
                                                 _Source.FullName.Length));

                try
                {
                    // Prevent IOException
                    if (!FolderPath.Exists) FolderPath.Create(); 

                    OnItemCopied(new ItemCopiedEventArgs(
                            FolderSourceList[iterator].FullName,
                            FolderPath.FullName,
                            0,
                            iterator,
                            FolderSourceList.Count,
                            true));
                }
                catch (Exception iError)
                {
                    OnCopyError(new CopyErrorEventArgs(
                            FolderSourceList[iterator].FullName,
                            FolderPath.FullName,
                            iError));
                }
            }
            
        }



        // Part 3: Source to Destination File Copy
        ///////////////////////////////////////////////////////

        _CopierStatus = xDirectoryStatus.CopyingFiles;

        for (iterator = 0; iterator < FileSourceList.Count; iterator++)
        {
                if (_CancelCopy) return;

                if (FileSourceList[iterator].Exists)
                {
                    FilePath = new FileInfo(
                        _Destination.FullName +
                        Path.DirectorySeparatorChar +
                        FileSourceList[iterator].FullName.Remove(0, 
                                             _Source.FullName.Length + 1));

                    try
                    {
                        if (_Overwrite)
                            FileSourceList[iterator].CopyTo(FilePath.FullName,
                                                            true); 
                        else
                        {
                            if (!FilePath.Exists)
                                FileSourceList[iterator].CopyTo(
                                                    FilePath.FullName, true);
                        }

                        OnItemCopied(new ItemCopiedEventArgs(
                                FileSourceList[iterator].FullName,
                                FilePath.FullName,
                                FileSourceList[iterator].Length,
                                iterator,
                                FileSourceList.Count,
                                false));

                    }
                    catch (Exception iError)
                    {
                        OnCopyError(new CopyErrorEventArgs(
                                FileSourceList[iterator].FullName,
                                FilePath.FullName,
                                iError));
                    }
                }
            
        }

    }
    catch
    { throw; }
    finally
    {
        _CopierStatus = xDirectoryStatus.Stopped;
        OnCopyComplete(new CopyCompleteEventArgs(_CancelCopy));
    }
}

Old Version 2.0 Description

Written May 18th, 2006

Updated July 25th, 2006 - Fixed a major (but minute) error, added a ... + @"\" + ... to the string sFolderPath = ... and string sFilePath = ... declarations. Should fix any "files with foldername copied" errors.

After so much feedback, I decided I would revisit the Copy code. It's been quite a while since I posted the original, and I've learned a great deal more in the intervening time with regards to programming, and so I hope to bring this method to a new level.

The original version was written in .NET 1.1, and since then, I've gotten used to .NET 2.0, so this is what the second version is written in. Everyone cheer for Generic Collections!

Also, if you notice in the source code, the xDirectory.Copy method is now iterative instead of recursive. The iterative version of this code performs better than the recursive (for obvious reasons), and actually uses less overall memory.

Some key points were brought up, both in my work and in the comments; hopefully, all these have been rectified to everyone's satisfaction!

  • When the Overwrite variable was set to false, if any file was found to exist in the destination directory, the entire method would rrror out with an IOException.
  • The recursive nature of the original version meant encountering a never-ending recursive loop if the destination directory was inside of the source directory (say, creating a backup of a folder and putting it in the folder/backup.) The iterative version gets a full listing of the source directory first, before the copy begins, and then during the copy, makes sure that if a file has been moved in the small interval between the read and the copy, the whole method won't simply error out.
  • I introduced error handling into the method for common problems. Relieves some of the work for the weary coder.

Here's the xDirectory.Copy() method in all its glory. (There are overloads in the source project.)

/// <span class="code-SummaryComment"><summary></span>

/// xDirectory.Copy() - Copy a Source Directory
/// and it's SubDirectories/Files
/// <span class="code-SummaryComment"></summary></span>
/// <span class="code-SummaryComment"><param name="diSource">The Source Directory</param></span>
/// <span class="code-SummaryComment"><param name="diDestination">The Destination Directory</param></span>
/// <span class="code-SummaryComment"><param name="FileFilter">The File Filter</span>
/// (Standard Windows Filter Parameter, Wildcards: "*" and "?")<span class="code-SummaryComment"></param></span>

/// <span class="code-SummaryComment"><param name="DirectoryFilter">The Directory Filter</span>
/// (Standard Windows Filter Parameter, Wildcards: "*" and "?")<span class="code-SummaryComment"></param></span>
/// <span class="code-SummaryComment"><param name="Overwrite">Whether or not to Overwrite</span>
/// a Destination File if it Exists.<span class="code-SummaryComment"></param></span>
/// <span class="code-SummaryComment"><param name="FolderLimit">Iteration Limit - Total Number</span>
/// of Folders/SubFolders to Copy<span class="code-SummaryComment"></param></span>
public static void Copy(DirectoryInfo diSource, 
       DirectoryInfo diDestination, 
       string FileFilter, string DirectoryFilter, 
       bool Overwrite, int FolderLimit)
{
    int iterator = 0;
    List<DirectoryInfo> diSourceList = 
            new List<DirectoryInfo>();
    List<FileInfo> fiSourceList = 
            new List<FileInfo>();

    try
    {
        ///// Error Checking /////
        if (diSource == null) 
            throw new ArgumentException("Source Directory: NULL");
        if (diDestination == null) 
            throw new ArgumentException("Destination Directory: NULL");
        if (!diSource.Exists) 
            throw new IOException("Source Directory: Does Not Exist");
        if (!(FolderLimit > 0)) 
            throw new ArgumentException("Folder Limit: Less Than 1");
        if (DirectoryFilter == null || DirectoryFilter == string.Empty)
            DirectoryFilter = "*";
        if (FileFilter == null || FileFilter == string.Empty)
            FileFilter = "*";

        ///// Add Source Directory to List /////
        diSourceList.Add(diSource);

        ///// First Section: Get Folder/File Listing /////
        while (iterator < diSourceList.Count && iterator < FolderLimit)
        {
            foreach (DirectoryInfo di in 
              diSourceList[iterator].GetDirectories(DirectoryFilter))
                diSourceList.Add(di);

            foreach (FileInfo fi in 
                                diSourceList[iterator].GetFiles(FileFilter))
                fiSourceList.Add(fi);

            iterator++;
        }

        ///// Second Section: Create Folders from Listing /////
        foreach (DirectoryInfo di in diSourceList)
        {
            if (di.Exists)
            {
                string sFolderPath = diDestination.FullName + @"\" +
                       di.FullName.Remove(0, diSource.FullName.Length);
                
                ///// Prevent Silly IOException /////
                if (!Directory.Exists(sFolderPath))
                    Directory.CreateDirectory(sFolderPath);
            }
        }

        ///// Third Section: Copy Files from Listing /////
        foreach (FileInfo fi in fiSourceList)
        {
            if (fi.Exists)
            {
                string sFilePath = diDestination.FullName + @"\" +
                       fi.FullName.Remove(0, diSource.FullName.Length);
                
                //// Better Overwrite Test W/O IOException from CopyTo() ////
                if (Overwrite)
                    fi.CopyTo(sFilePath, true);
                else
                {
                    ///// Prevent Silly IOException /////
                    if (!File.Exists(sFilePath))
                        fi.CopyTo(sFilePath, true);
                }
            }
        }
    }
    catch
    { throw; }
}

Hopefully, you'll be able to put the new code to work easier than before! I've left both versions on here just in case .NET 2.0 is out of your reach; though, if pressed, you can use this code in .NET 1.1 with just a little tweaking. Essentially, change the generic List<DirectoryInfo> to an ArrayList, and do some type-casting to get it to work.

Enjoy!

Version History

  • Version 3 [.NET 2.0] - Written October 18th, 2006
  • Version 2 [.NET 2.0] - Updated July 25th, 2006
  • Version 2 [.NET 2.0] - Written May 18th, 2006
  • Version 1 [.NET 1.1] - Written April 6th, 2005

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

John Storer II
Software Developer Employer's Security
United States United States
I'm a Computer Support Technician for a Indiana School System. Really, I'm a C#/ASP.NET Developer in a Software Support coat. Jack-of-all-trades, I do Hardware Support, Software Support, Programming in C#.NET, ASP.NET / Javascript, Web Design, Graphic Arts and lots of other stuff.

I've been programming almost 21 years now, and have used more than 10 different languages in the past.

You may also be interested in...

Comments and Discussions

 
Question2 bugs(?) Pin
Member 867899926-Feb-12 6:43
memberMember 867899926-Feb-12 6:43 
Questionany updates ?? Pin
alhambra-eidos10-Mar-11 0:37
memberalhambra-eidos10-Mar-11 0:37 
AnswerCode to copy folders and SubFolders with files. Pin
Member 188450412-Jan-12 0:00
memberMember 188450412-Jan-12 0:00 
GeneralRe: Code to copy folders and SubFolders with files. Pin
John Storer II12-Jan-12 1:03
memberJohn Storer II12-Jan-12 1:03 
AnswerRe: any updates ?? Pin
John Storer II12-Jan-12 1:06
memberJohn Storer II12-Jan-12 1:06 
GeneralTest App Error Handling Not Working [modified] Pin
mason.c15-Jul-10 8:16
membermason.c15-Jul-10 8:16 
Questioncopy multiple folder simultaneously Pin
c13l18-May-10 16:37
memberc13l18-May-10 16:37 
AnswerRe: copy multiple folder simultaneously Pin
John Storer II9-Nov-10 5:05
memberJohn Storer II9-Nov-10 5:05 
QuestionFolder filter.....errrrmmm? Pin
riscy12-Feb-10 20:40
memberriscy12-Feb-10 20:40 
AnswerRe: Folder filter.....errrrmmm? Pin
John Storer II9-Nov-10 5:19
memberJohn Storer II9-Nov-10 5:19 
GeneralError Message: System.IO.PathTooLongException (Path and/or Filename is too long) Pin
pebo664507-Dec-09 20:39
memberpebo664507-Dec-09 20:39 
GeneralRe: Error Message: System.IO.PathTooLongException (Path and/or Filename is too long) Pin
John Storer II8-Dec-09 1:26
memberJohn Storer II8-Dec-09 1:26 
GeneralRe: Error Message: System.IO.PathTooLongException (Path and/or Filename is too long) Pin
pebo664508-Dec-09 2:11
memberpebo664508-Dec-09 2:11 
Generalsome bug in version 3 Pin
shenjian2508-Nov-09 7:56
membershenjian2508-Nov-09 7:56 
QuestionQuestion about me converting your utility to VB.Net Pin
Jim Tyler14-Oct-09 6:45
memberJim Tyler14-Oct-09 6:45 
AnswerRe: Question about me converting your utility to VB.Net Pin
Jim Tyler14-Oct-09 7:08
memberJim Tyler14-Oct-09 7:08 
GeneralRe: Question about me converting your utility to VB.Net Pin
John Storer II14-Oct-09 7:14
memberJohn Storer II14-Oct-09 7:14 
GeneralRe: Question about me converting your utility to VB.Net Pin
Jim Tyler14-Oct-09 7:22
memberJim Tyler14-Oct-09 7:22 
GeneralEnhancements I made Pin
jverbarg13-Oct-08 5:54
memberjverbarg13-Oct-08 5:54 
GeneralRe: Enhancements I made Pin
Jan R Hansen23-Sep-09 1:06
memberJan R Hansen23-Sep-09 1:06 
GeneralRe: Enhancements I made Pin
jverbarg23-Sep-09 2:26
memberjverbarg23-Sep-09 2:26 
GeneralRe: Enhancements I made Pin
John Storer II23-Sep-09 4:28
memberJohn Storer II23-Sep-09 4:28 
GeneralRe: Enhancements I made Pin
amthamang22-Sep-10 9:18
memberamthamang22-Sep-10 9:18 
GeneralRe: Enhancements I made Pin
vikramhaibatti11-Dec-11 21:46
membervikramhaibatti11-Dec-11 21:46 
Generalgreat utility Pin
dmitriy_burdan21-Jul-08 9:54
memberdmitriy_burdan21-Jul-08 9:54 
Questionexiting appliction while copying large file will cause uncomplete copy. Pin
depaniy1-Jul-08 13:34
memberdepaniy1-Jul-08 13:34 
JokeRe: exiting appliction while copying large file will cause uncomplete copy. Pin
P3 Tech27-Jan-09 7:15
memberP3 Tech27-Jan-09 7:15 
QuestionGood program but can be done in DOS easily... Pin
Member 373780311-May-08 22:29
memberMember 373780311-May-08 22:29 
AnswerRe: Good program but can be done in DOS easily... Pin
John Storer II12-May-08 4:03
memberJohn Storer II12-May-08 4:03 
AnswerRe: Good program but can be done in DOS easily... Pin
Dionito18-Jun-08 11:17
memberDionito18-Jun-08 11:17 
General:-S Pin
Chris Nevill7-May-08 1:36
memberChris Nevill7-May-08 1:36 
GeneralRe: :-S Pin
John Storer II12-May-08 4:06
memberJohn Storer II12-May-08 4:06 
GeneralRe: :-S Pin
Chris Nevill12-May-08 4:10
memberChris Nevill12-May-08 4:10 
AnswerRe: :-S Pin
Chris Nevill12-May-08 4:11
memberChris Nevill12-May-08 4:11 
General_CopierStatus not set when StartCopy is called Pin
Chris Nevill30-Apr-08 6:08
memberChris Nevill30-Apr-08 6:08 
GeneralRe: _CopierStatus not set when StartCopy is called Pin
John Storer II12-May-08 4:07
memberJohn Storer II12-May-08 4:07 
GeneralTrying to convert to vb.net Pin
Christian Muggli20-Apr-08 9:29
memberChristian Muggli20-Apr-08 9:29 
GeneralRe: Trying to convert to vb.net Pin
John Storer II20-Apr-08 16:02
memberJohn Storer II20-Apr-08 16:02 
GeneralRe: Trying to convert to vb.net Pin
Jim Tyler14-Oct-09 7:10
memberJim Tyler14-Oct-09 7:10 
Generalmultiple filetypes copy to multiple folders Pin
UltraWhack14-Apr-08 7:25
memberUltraWhack14-Apr-08 7:25 
GeneralGood job Pin
nirvansk8157-Apr-08 15:06
membernirvansk8157-Apr-08 15:06 
GeneralCopy Specific filetypes out of sub/folders to a folder Pin
UltraWhack2-Apr-08 18:49
memberUltraWhack2-Apr-08 18:49 
GeneralRe: Copy Specific filetypes out of sub/folders to a folder Pin
John Storer II3-Apr-08 3:36
memberJohn Storer II3-Apr-08 3:36 
GeneralRe: Copy Specific filetypes out of sub/folders to a folder Pin
UltraWhack3-Apr-08 10:29
memberUltraWhack3-Apr-08 10:29 
GeneralRe: Copy Specific filetypes out of sub/folders to a folder Pin
boujouxman23-Jun-08 0:03
memberboujouxman23-Jun-08 0:03 
GeneralCopy the root folder too Pin
Diego Kober18-Mar-08 7:43
memberDiego Kober18-Mar-08 7:43 
GeneralProblem with network drive Pin
vtomer8-Nov-07 21:52
membervtomer8-Nov-07 21:52 
GeneralRe: Problem with network drive Pin
John Storer II9-Nov-07 1:52
memberJohn Storer II9-Nov-07 1:52 
GeneralRe: Problem with network drive Pin
vtomer10-Nov-07 20:17
membervtomer10-Nov-07 20:17 
GeneralRe: Problem with network drive Pin
JaySRiggs16-Nov-07 7:20
memberJaySRiggs16-Nov-07 7:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160721.1 | Last Updated 20 Oct 2006
Article Copyright 2005 by John Storer II
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid