18

I want to be able to get the size of one of the local directories using C#. I'm trying to avoid the following (pseudo like code), although in the worst case scenario I will have to settle for this:

    int GetSize(Directory)
    {
        int Size = 0;

        foreach ( File in Directory )
        {
            FileInfo fInfo of File;
            Size += fInfo.Size;
        }

        foreach ( SubDirectory in Directory )
        {
            Size += GetSize(SubDirectory);
        }
        return Size;
    }

Basically, is there a Walk() available somewhere so that I can walk through the directory tree? Which would save the recursion of going through each sub-directory.

Lloyd Powell
  • 18,270
  • 17
  • 87
  • 123

9 Answers9

43

A very succinct way to get a folder size in .net 4.0 is below. It still suffers from the limitation of having to traverse all files recursively, but it doesn't load a potentially huge array of filenames, and it's only two lines of code. Make sure to use the namespaces System.IO and System.Linq.

private static long GetDirectorySize(string folderPath)
{
    DirectoryInfo di = new DirectoryInfo(folderPath);
    return di.EnumerateFiles("*.*", SearchOption.AllDirectories).Sum(fi => fi.Length);
}
Penguin
  • 311
  • 2
  • 14
Kev
  • 1,832
  • 1
  • 19
  • 24
  • 11
    I recommend you use * instead of * . * because file names do not require a . – Joe Aug 23 '11 at 21:01
  • 7
    I follow your reasoning Joe, but I think it is unnecessary because Windows OS will match the * . * pattern against file/folder names regardless of whether they contain a '.' or not. I think that it makes no difference, but would be interested to know if I'm wrong. Of course, if it was Mono running on a different OS then it may well be different. – Kev Sep 11 '11 at 19:45
  • 2
    Very nice. Unless you want to handle UnauthorizedAccessException, PathTooLongException, etc on a per-directory basis. – Phil Haselden Jul 05 '17 at 01:53
  • If coding in Visual Basic: di.EnumerateFiles("*.*", IO.SearchOption.AllDirectories).Sum(Function(fi) fi.Length) – qfactor77 Jun 08 '20 at 16:38
20

If you use Directory.GetFiles you can do a recursive seach (using SearchOption.AllDirectories), but this is a bit flaky anyway (especially if you don't have access to one of the sub-directories) - and might involve a huge single array coming back (warning klaxon...).

I'd be happy with the recursion approach unless I could show (via profiling) a bottleneck; and then I'd probably switch to (single-level) Directory.GetFiles, using a Queue<string> to emulate recursion.

Note that .NET 4.0 introduces some enumerator-based file/directory listing methods which save on the big arrays.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
6

Here my .NET 4.0 approach

public static long GetFileSizeSumFromDirectory(string searchDirectory)
{
 var files = Directory.EnumerateFiles(searchDirectory);

 // get the sizeof all files in the current directory
 var currentSize = (from file in files let fileInfo = new FileInfo(file) select fileInfo.Length).Sum();

 var directories = Directory.EnumerateDirectories(searchDirectory);

 // get the size of all files in all subdirectories
 var subDirSize = (from directory in directories select GetFileSizeSumFromDirectory(directory)).Sum();

 return currentSize + subDirSize;
}

Or even nicer:

// get IEnumerable from all files in the current dir and all sub dirs
var files = Directory.EnumerateFiles(searchDirectory,"*",SearchOption.AllDirectories);

// get the size of all files
long sum = (from file in files let fileInfo = new FileInfo(file) select fileInfo .Length).Sum();

As Gabriel pointed out this will fail if you have a restricted directory under the searchDirectory!

flayn
  • 5,272
  • 4
  • 48
  • 69
  • 1
    Small typo on the last code line, should be: // get the size of all files long sum = (from file in files let fileInfo = new FileInfo(file) select fileInfo.Length).Sum(); – Gabriel Mongeon Jul 29 '10 at 19:27
  • 1
    And if you have a restricted directory under the searchDirectory, it will fail! To see that resolve in a future version of the framework: https://connect.microsoft.com/VisualStudio/feedback/details/512171/directory-enumeratedirectory-etc-unusable-due-to-frequent-unauthorizedaccessexceptions-even-runas-administrator – Gabriel Mongeon Jul 29 '10 at 19:49
  • Thanks for your comments Gabriel. I have fixed the typo and included the possible fail warning. – flayn Aug 01 '10 at 21:25
4

You could hide your recursion behind an extension method (to avoid the issues Marc has highlighted with the GetFiles() method):

public static class UserExtension
{
  public static IEnumerable<FileInfo> Walk(this DirectoryInfo directory)
  {
    foreach(FileInfo file in directory.GetFiles())
    {
      yield return file;
    }

    foreach(DirectoryInfo subDirectory in directory.GetDirectories())
    { 
      foreach(FileInfo file in subDirectory.Walk())
      {
        yield return file;
      }
    }
  }
}

(You probably want to add some exception handling to this for protected folders etc.)

Then:

using static UserExtension;

long totalSize = 0L;
var startFolder = new DirectoryInfo("<path to folder>");

// iteration
foreach(FileInfo file in startFolder.Walk())
{
    totalSize += file.Length;
}

// linq
totalSize = di.Walk().Sum(s => s.Length);

Basically the same code, but maybe a little neater...

Bernhard
  • 2,541
  • 1
  • 26
  • 24
Martin Harris
  • 28,277
  • 7
  • 90
  • 101
3

First, forgive my poor english ;o) I had a problem that took me to this page : enumerate files of a directory and his subdirectories without blocking on an UnauthorizedAccessException, and, like the new method of .Net 4 DirectoryInfo.Enumerate..., get the first result before the end of the entire query.

With the help of various examples found here and there on the web, I finally write this method :

public static IEnumerable<FileInfo> EnumerateFiles_Recursive(this DirectoryInfo directory, string searchPattern, SearchOption searchOption, Func<DirectoryInfo, Exception, bool> handleExceptionAccess)
{
    Queue<DirectoryInfo> subDirectories = new Queue<DirectoryInfo>();
    IEnumerable<FileSystemInfo> entries = null;

    // Try to get an enumerator on fileSystemInfos of directory
    try
    {
        entries = directory.EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
    }
    catch (Exception e)
    {
        // If there's a callback delegate and this delegate return true, we don't throw the exception
        if (handleExceptionAccess == null || !handleExceptionAccess(directory, e))
            throw;
        // If the exception wasn't throw, we make entries reference an empty collection
        entries = EmptyFileSystemInfos;
    }

    // Yield return file entries of the directory and enqueue the subdirectories
    foreach (FileSystemInfo entrie in entries)
    {
        if (entrie is FileInfo)
            yield return (FileInfo)entrie;
        else if (entrie is DirectoryInfo)
            subDirectories.Enqueue((DirectoryInfo)entrie);
    }

    // If recursive search, we make recursive call on the method to yield return entries of the subdirectories.
    if (searchOption == SearchOption.AllDirectories)
    {
        DirectoryInfo subDir = null;
        while (subDirectories.Count > 0)
        {
            subDir = subDirectories.Dequeue();
            foreach (FileInfo file in subDir.EnumerateFiles_Recursive(searchPattern, searchOption, handleExceptionAccess))
            {
                yield return file;
            }
        }
    }
    else
        subDirectories.Clear();
}

I use a Queue and a recursive method to keep traditional order (content of directory and then content of first subdirectory and his own subdirectories and then content of the second...). The parameter "handleExceptionAccess" is just a function call when an exception is thrown with a directory; the function must return true to indicate that the exception must be ignored.

With this methode, you can write :

DirectoryInfo dir = new DirectoryInfo("c:\\temp");
long size = dir.EnumerateFiles_Recursive("*", SearchOption.AllDirectories, (d, ex) => true).Sum(f => f.Length);

And here we are : all exception when trying to enumerate a directory will be ignore !

Hope this help

Lionel

PS : for a reason I can't explain, my method is more quick than the framework 4 one...

PPS : you can get my test solutions with source for those methods : here TestDirEnumerate. I write EnumerateFiles_Recursive, EnumerateFiles_NonRecursive (use a queue to avoid recursion) and EnumerateFiles_NonRecursive_TraditionalOrder (use a stack of queue to avoid recursion and keep traditional order). Keep those 3 methods has no interest, I write them only for test the best one. I think to keep only the last one. I also wrote the equivalent for EnumerateFileSystemInfos and EnumerateDirectories.

  • I done a misstake in this version because I search sudirectories with the searchPattern. So If a subdirectorie doesn't match with searchPattern, I don't explore it...[br] I wrote new version to correct this : but, I must do two request on directory : one with searchPattern, and another without searchPattern ==> performance are not as good as before. – Lionel KOMSA Mar 27 '11 at 15:22
  • So I add a version without searchpattern which are faster. Look at my test solution here : [link](http://lionel.komsa.free.fr/temp/TestDirEnumerate2.zip) – Lionel KOMSA Mar 27 '11 at 15:28
  • Perhaps I'm missing something, but with this method you'll still get incomplete listings, right? If I am authorized to see folders A and C but not B, and `Enumerate` examines A, then B, then C, it'll stop with B and fail to count C. –  Nov 02 '11 at 21:24
1

Have a look at this post:

http://social.msdn.microsoft.com/forums/en-US/vbgeneral/thread/eed54ebe-facd-4305-b64b-9dbdc65df04e

Basically there is no clean .NET way, but there is a quite straightforward COM approach so if you're happy with using COM interop and being tied to Windows, this could work for you.

Tamas Czinege
  • 118,853
  • 40
  • 150
  • 176
1

the solution is already here https://stackoverflow.com/a/12665904/1498669

as in the duplicate How do I Get Folder Size in C#? shown -> you can do this also in c#

first, add the COM reference "Microsoft Scripting Runtime" to your project and use:

var fso = new Scripting.FileSystemObject();
var folder = fso.GetFolder(@"C:\Windows");
double sizeInBytes = folder.Size;
// cleanup COM
System.Runtime.InteropServices.Marshal.ReleaseComObject(folder);
System.Runtime.InteropServices.Marshal.ReleaseComObject(fso);

remember to cleanup the COM references

Bernhard
  • 2,541
  • 1
  • 26
  • 24
0

I've been looking some time ago for a function like the one you ask for and from what I've found on the Internet and in MSDN forums, there is no such function.

The recursive way is the only I found to obtain the size of a Folder considering all the files and subfolders that contains.

Doliveras
  • 1,794
  • 2
  • 14
  • 30
-3

You should make it easy on yourself. Make a method and passthrough the location of the directory.

    private static long GetDirectorySize(string location) {
        return new DirectoryInfo(location).GetFiles("*.*", SearchOption.AllDirectories).Sum(file => file.Length);
    }

-G

TGarrett
  • 562
  • 4
  • 15