Iterating through a bunch of folders and files

So you want to start at a top level folder, and then process all the folders beneath… Maybe you really do want to look at every file (maybe count the total size of the folder), maybe you want to process all the XML files there. The most obvious route is to recursively search through each folder;

        static void Main(string[] args)
        {
            string startFolder = @"C:\temp";

            List<string> contents = new List<string>();
            foreach (string dir in Directory.GetDirectories(startFolder))
                ProcessFolder(dir, contents);

            foreach (string fileName in contents)
                Console.WriteLine(fileName);

            Console.ReadKey();
        }

        static void ProcessFolder(string folder, IList<string> theList)
        {
            foreach (string file in Directory.GetFiles(folder))
                theList.Add(folder + "\\" + file);

            foreach (string dir in Directory.GetDirectories(folder))
                ProcessFolder(dir, theList);
        }

All well and good but, at some point (probably due to the depth of the file system) you will run out of stack space.  A better way to traverse the folder structure is to do an iterative search;

        private void ProcessFolder(string startingPath)
        {
            int iterator = 0;
            List<string> dirList = new List<string>();
            dirList.Add(startingPath);
            string parentFolder = startingPath;

            // Every new folder found is added to the list to be searched. Continue until we have
            // found, and reported on, every folder or the calling thread wants us to stop
            while (iterator < dirList.Count && !(workerThreadInfo.StopRequested))
            {
                parentFolder = dirList[iterator];       // Each FileTreeEntry wants to know who its parent is

                try
                {
                    foreach (string dir in Directory.GetDirectories(dirList[iterator]))
                    {
                        AddFolder(parentFolder, dir, dir);
                        dirList.Add(dir);
                    }

                    foreach (string filename in Directory.GetFiles(dirList[iterator]))
                    {
                        FileInfo file = new FileInfo(filename);
                        AddFile(parentFolder, file.Name, file.Length);
                    }
                }
                // There are two *acceptable* exceptions that we may see, but should not consider fatal
                catch (UnauthorizedAccessException)
                {
                }
                catch (PathTooLongException)
                {
                }
                iterator++;
            }
        }

Now, we iterate through each discovered folder and for each discovered file, we call an external routine (in this case “AddFile()”. Note the two caught exceptions which can occur but which, in the author’s opinion, are not important in this context;

  • UnauthorizedAccessException
    • OK; ya got me. I’m not allowed in here, so let’s continue and not break the calling app
  • PathTooLongException
    • This is a funny one. Win200x sets a maximum path length of 255 characters. Create a big and complex structure (especially a Java one), zip it and then unravel it under a folder that is maybe 100 characters long. Windows is happy to unzip this, and even display it in the folder view. But try and open the file and you’ll be stuffed

 

So, ignoring these, this routine will handle any files within a structure, irrespective of how deep that structure gets.

11 comments to Iterating through a bunch of folders and files

  • mkamoski

    Thank you for the code. One may want to consider Path.Combine when building file paths, because it helps one to avoid errors. Thank you. — Mark Kamoski

  • Skot

    Thanks Mark – glad to have the feedback.
    I'm not sure where you're suggesting that I use Path.Combine, though?

    Cheers
    Scott

  • Anonymous

    Which namespace or object does the AddFolder belong to?

  • Fagelot

    Good writing but need some help though.
    I want to implement the iteration in one of my application but my problem is that I don't know how to make the the external routing "addFolder" and "addFile".
    If you can give one example, it should be appreciated.

    Many thanks, and again thanks for the script.

  • Anonymous

    theList.Add( System.IO.Path.Combine(folder,file));

  • Skot

    @Anonymous and @Fagelot
    sorry for the delay in responding – I thought blogspot would send me updates but it seems it doesn't!

    Anyways, AddFolder and AddFile, in this example, are your own code where you are interested in the discovery of a new folder or file. Chances are that you only care about a newly discovered file, so you could a) delete the call to AddFolder() and b) add your own AddFile() routine.

    How I handle this is to use AddFile() to add file paths to a list and when the core search routine ends, I then work thru that list. If you were adventurous you could kick off the file handlers on a separate thread while the search routine was still running.

    Hope that helps, and thanks for listening and commenting!

  • Skot

    @anonymous (17th Nov 2009) – not sure what you're suggesting? The point of the code is to add discovered paths to a list…

  • Hi Skot that is Great post but i have two questions/
    1- I have a task that move files from computer to SAN Storage How can I get the file name Immidiatly?
    2- I want to Insert the file name and metadata inside SQL DB
    Thanks!

  • Thanks for the comments, Samy. Umm, I don't really know what to advise for either of your questions. This techique / code works on anything that Windows recognises as a file share. If that is on SAN storage then I'd expect it to just work. What, exactly are you trying to do?

    On the second, I'm guessing you want to run my code and insert into a database? If so, then this is slightly trickier but only because (without wanting to cause offence) if you don't know how to insert a record, then you won't know how to retrieve it.

    Anyway (deep breath); I haven't tried the following so this is just suggested code. If you have issues then you should really google "C# insert row"  and "C# read row";

    Start before the iteration with opening the database (note that the schema needs to already exist);

        using System.Data;                      // State variables
        using System.Data.ADO;                  // Database
        using System.Globalization;             // Date

     public const string DB_CONN_STRING =
     "Driver={Microsoft Access Driver (*.mdb)}; "+
     "DBQ=D:\\CS\\TestDbReadWrite\\SimpleTest.mdb";

     ADOConnection conn = new ADOConnection(DB_CONN_STRING);
        conn.Open();

    Then, iterate through the file returned and write them to your database;

     String sSQLCommand = "INSERT INTO File (folder, name, length)
                             "VALUES( " + parentFolder, file.Name, file.Length);
        // Create the command object
        ADOCommand cmdAdder = new ADOCommand(
            sSQLCommand,
            DB_CONN_STRING);
        cmdAdder.ActiveConnection.Open();
        // Execute the SQL command
        int nNoAdded = cmdAdder.ExecuteNonQuery();

     

    (thanks to http://www.codeproject.com/KB/database/simpledbreadwrite.aspx for some boilerplate code here)

     

  • Christian

    Hi Scott,
    would it be possible to publish the code for the "addFolder" and "addFile" procedure?
    Maybe a complete sample code would make your article much easier to reproduce.
    Chris

  • Hi Christian

    apologies for the delay in responding. The addFolder and addFile methods tend to be application specific. In this case, the iterator was part of an application that was displaying volume usage as a tree diagram. As an aside, the entire source is available at http://www.scottleckie.com/2010/04/filetreeview_source_1-0-0-0/

    The methods that are called for addFolder and addFile are shown below

     

    /// <summary>

    /// Creates a new <see cref="FileTreeEntry"/> folder object, based

    /// on the info provided in name and path, and adds it to the

    /// <see cref="FileTreeEntry"/> object that is the parent folder.

    /// <para>The parent folder is looked up by checking the dictionary

    /// of previously discovered folders for a matching path. The parent

    /// must already have been created!</para>

    /// </summary>

    /// <param name="parent">Path of the parent folder</param>

    /// <param name="name">Name of the folder to be added</param>

    /// <param name="path">Full path of the folder to ne added (parent + name)</param>

    /// <returns>The <see cref="FileTreeEntry"/> of the new folder</returns>

    /// <exception cref="ArgumentException">

    /// One of the parent, name or path parameters is null or empty

    /// </exception>

    /// <exception cref="KeyNotFoundException">

    /// The <see cref="FileTreeEntry"/> referred to by the parent

    /// parameter does not refer to a previously discovered folder

    /// </exception>

    private FileTreeEntry AddFolder(string parent, string name, string path)

    {

    if (string.IsNullOrEmpty(parent))

    throw new ArgumentException("Must supply a parent folder");

    if (string.IsNullOrEmpty(name))

    throw new ArgumentException("Must supply a name");

    if (string.IsNullOrEmpty(path))

    throw new ArgumentException("Must supply a path");

    FileTreeEntry parentFolder = listOfFolders[parent];

    FileTreeEntry newFolder = parentFolder.AddFolder(name, path);

    listOfFolders.Add(path, newFolder);

    listOfEntries.Add(newFolder);

    workerThreadInfo.NumFolders++;

    return newFolder;

    }

    /// <summary>

    /// Creates a new <see cref="FileTreeEntry"/> file object, based

    /// on the info provided in name, and adds it to the

    /// <see cref="FileTreeEntry"/> object that is the parent folder.

    /// <para>The parent folder is looked up by checking the dictionary

    /// of previously discovered folders for a matching path. The parent

    /// must already have been created!</para>

    /// </summary>

    /// <param name="parent">Path of the parent folder</param>

    /// <param name="name">Name of the file to be added</param>

    /// <param name="size">Size of the file to be added</param>

    /// <returns>The <see cref="FileTreeEntry"/> of the new file</returns>

    /// <exception cref="ArgumentException">

    /// One of the parent or name parameters is null or empty

    /// </exception>

    /// <exception cref="KeyNotFoundException">

    /// The <see cref="FileTreeEntry"/> referred to by the parent

    /// parameter does not refer to a previously discovered folder

    /// </exception>

    private FileTreeEntry AddFile(string parent, string name, long size)

    {

    if (string.IsNullOrEmpty(parent))

    throw new ArgumentException("Must supply a parent folder");

    if (string.IsNullOrEmpty(name))

    throw new ArgumentException("Must supply a name");

    FileTreeEntry parentFolder = listOfFolders[parent];

    FileTreeEntry newFile = parentFolder.AddFile(name, size);

    listOfEntries.Add(newFile);

    workerThreadInfo.NumFiles++;

    workerThreadInfo.TotalFileSize += (ulong) size;

    return newFile;

    }

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <font color="" face="" size=""> <span style="">