What's the Best Way to Calculate the Size of a Directory in .Net

What's the best way to calculate the size of a directory in .NET?

I do not believe there is a Win32 API to calculate the space consumed by a directory, although I stand to be corrected on this. If there were then I would assume Explorer would use it. If you get the Properties of a large directory in Explorer, the time it takes to give you the folder size is proportional to the number of files/sub-directories it contains.

Your routine seems fairly neat & simple. Bear in mind that you are calculating the sum of the file lengths, not the actual space consumed on the disk. Space consumed by wasted space at the end of clusters, file streams etc, are being ignored.

What’s the best way to calculate the size of a directory in VB .NET?

Though this answer is talking about Python, the concept applies here as well.

Windows Explorer uses system API calls FindFirstFile and FindNextFile recursively to pull file information, and then can access the file sizes very quickly through the data that's passed back via a struct, WIN32_FIND_DATA: http://msdn.microsoft.com/en-us/library/aa365740(v=VS.85).aspx.

My suggestion would be to implement these API calls using P/Invoke, and I believe you will experience significant performance gains.

Directory file size calculation - how to make it faster?

If fiddled with it a while, trying to Parallelize it, and surprisingly - it speeded up here on my machine (up to 3 times on a quadcore), don't know if it is valid in all cases, but give it a try...

.NET4.0 Code (or use 3.5 with TaskParallelLibrary)

    private static long DirSize(string sourceDir, bool recurse)
{
long size = 0;
string[] fileEntries = Directory.GetFiles(sourceDir);

foreach (string fileName in fileEntries)
{
Interlocked.Add(ref size, (new FileInfo(fileName)).Length);
}

if (recurse)
{
string[] subdirEntries = Directory.GetDirectories(sourceDir);

Parallel.For<long>(0, subdirEntries.Length, () => 0, (i, loop, subtotal) =>
{
if ((File.GetAttributes(subdirEntries[i]) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint)
{
subtotal += DirSize(subdirEntries[i], true);
return subtotal;
}
return 0;
},
(x) => Interlocked.Add(ref size, x)
);
}
return size;
}

How do I get a directory size (files in the directory) in C#?

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.

Calculating directory sizes

You can utilize Parallel.ForEach to run the directory size calculation in parallel fashion. You can get the GetDirectories and run the Parallel.ForEach on each node. You can use a variable to keep track of size and display that to the user. Each parallel calculation would be incrementing on the same variable. If needed use lock() to synchronize between parallel executions.

How do I Get Folder Size in C#?

How about this:

private static long GetDirectorySize(string folderPath)
{
DirectoryInfo di = new DirectoryInfo(folderPath);
return di.EnumerateFiles("*", SearchOption.AllDirectories).Sum(fi => fi.Length);
}

from here.

This will give you the size in bytes; you will have to "prettify" that into GBs or MBs.

NOTE: This only works in .NET 4+.

EDIT: Changed the wildcard search from "*.*" to "*" as per the comments in the thread to which I linked. This should extend its usability to other OSes (if using Mono, for example).

Calculating folder size / Enumerate filesystem

You must enumerate the folder on a background thread.

Suggestions to improve performance

When using the DriveInfo API you can further improve the performance for the case that the folder path is a drive. In this case, you can omit the enumeration of the complete drive, which usually takes a while.

Furthermore, your current implementation aborts the calculation when the enumeration throws the UnauthorizedAccessException exception. You don't want that. You want the algorithm to ignore forbidden filesystem paths.

The following two examples show a fixed and improved version of your implementation.

The first solution targets the modern .NET Standard 2.1 compliant .NET versions.

The second solution targets the old .NET Framework.

.NET Standard 2.1 (.NET Core 3.0, .NET 5)

When using a .NET version compatible with .NET Standard 2.1 like .NET Core 3.0 and .NET 5 you can eliminate the exception handling. Using EnumerationOptions as an argument allows the API to ignore inaccessible directories, which significantly improves performance (no more UnauthorizedAccessException exceptions) and readability:

internal static async Task<bool> TryGetDirectorySize(string directoryPath, out long spaceUsedInBytes)
{
spaceUsedInBytes = -1;
var drives = DriveInfo.GetDrives();
DriveInfo targetDrive = drives.FirstOrDefault(drive => drive.Name.Equals(directoryPath, StringComparison.OrdinalIgnoreCase));

// Directory is a drive: skip the expensive enumeration of complete drive.
if (targetDrive != null)
{
spaceUsedInBytes = targetDrive.TotalSize - targetDrive.TotalFreeSpace;
return true;
}

if (!Directory.Exists(folderPath))
{
return false;
}

// Consider to make this local variable a private property
var enumerationOptions = new EnumerationOptions { RecurseSubdirectories = true };

var targetFolderInfo = new DirectoryInfo(directoryPath);
spaceUsedInBytes = await Task.Run(
() => targetFolderInfo.EnumerateFiles("*", enumerationOptions)
.Sum(fileInfo => fileInfo.Length));

return true;
}


.NET Framework

A .NET Framework compliant version. It fixes the issue with your original code where the enumeration is aborted as soon as an UnauthorizedAccessException exception is thrown. This version continues to enumerate all remaining directories using recursion:

internal static async Task<long> GetDirectorySize(string directoryPath)
{
long spaceUsedInBytes = -1;
var drives = DriveInfo.GetDrives();
DriveInfo targetDrive = drives.FirstOrDefault(drive => drive.Name.Equals(directoryPath, StringComparison.OrdinalIgnoreCase));

// Directory is a drive: skip enumeration of complete drive.
if (targetDrive != null)
{
spaceUsedInBytes = targetDrive.TotalSize - targetDrive.TotalFreeSpace;
return spaceUsedInBytes;
}

var targetDirectoryInfo = new DirectoryInfo(directoryPath);
spaceUsedInBytes = await Task.Run(() => SumDirectorySize(targetDirectoryInfo));
return spaceUsedInBytes;
}

private static long SumDirectorySize(DirectoryInfo parentDirectoryInfo)
{
long spaceUsedInBytes = 0;
try
{
spaceUsedInBytes = parentDirectoryInfo.EnumerateFiles("*", SearchOption.TopDirectoryOnly)
.Sum(fileInfo => fileInfo.Length);
}
catch (UnauthorizedAccessException)
{
return 0;
}

foreach (var subdirectoryInfo in parentDirectoryInfo.EnumerateDirectories("*", SearchOption.TopDirectoryOnly))
{
spaceUsedInBytes += SumDirectorySize(subdirectoryInfo);
}

return spaceUsedInBytes;
}


How to instantiate a type that requires to run async operations on construction

FolderModel.cs

class FolderModel
{
// Make a constructor private to force instantiation using the factory method
private FolderModel(string folderPath)
{
// Do non-async initialization
}

// Async factory method: add constructor parameters to async factory method
public static async Task<FolderModel> CreateAsync(string folderPath)
{
var instance = new FolderModel(folderPath);
await instance.InitializeAsync(folderPath);
return instance;
}

// Define member as protected virtual to allow derived classes to add initialization routines
protected virtual async Task InitializeAsync(string directoryPath)
{
// Consider to throw an exception here ONLY in case the folder is generated programmatically.
// If folder is retrieved from user input, use input validation
// or even better use a folder picker dialog
// to ensure that the provided path is always valid!
if (!Directory.Exists(directoryPath))
{
throw new DirectoryNotFoundException($"Invalid directory path '{directoryPath}'.");
}

long folderSize = await GetDirectorySize(directoryPath);

// TODO::Do something with the 'folderSize' value
// and execute other async code if necessary
}
}

Usage

// Create an instance of FolderModel example
private async Task SomeMethod()
{
// Always await async methods (methods that return a Task).
// Call static CreateAsync method instead of the constructor.
FolderModel folderModel = await FolderModel.CreateAsync(@"C:\");
}

In a more advanced scenario when you want to defer the initialization for example because you want to avoid to allocate expensive resources that are not needed now or never, you can make the instance call InitializeAsync when a certain member that depends on these resources is referenced or you can make the constructor and the InitializeAsync method public to allow the user of the class to call InitializeAsync explicitly.

How can I get total size of particular folder in C#?

You can use the following function to calculate the size of specific folder.

Source: https://askgif.com/blog/144/how-can-i-get-the-total-size-of-a-particular-folder-in-c/ (Courtesy: https://askgif.com/)

static String GetDriveSize(String ParticularFolder, String drive)
{
String size = "";
long MaxSpace = 10485760;
String rootfoldersize = @"~/userspace/" + ParticularFolder+ "/";
long totalbytes = 0;
long percentageusage = 0;

totalbytes = GetFolderSize(System.Web.HttpContext.Current.Server.MapPath(rootfoldersize) + "" + drive + "/");
percentageusage = (totalbytes * 100) / MaxSpace;
size = BytesToString(totalbytes);

return size;
}

static long GetFolderSize(string s)
{
string[] fileNames = Directory.GetFiles(s, "*.*");
long size = 0;

// Calculate total size by looping through files in the folder and totalling their sizes
foreach (string name in fileNames)
{
// length of each file.
FileInfo details = new FileInfo(name);
size += details.Length;
}
return size;
}

static String BytesToString(long byteCount)
{
string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB
if (byteCount == 0)
return "0" + suf[0];
long bytes = Math.Abs(byteCount);
int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
double num = Math.Round(bytes / Math.Pow(1024, place), 1);
return (Math.Sign(byteCount) * num).ToString() + suf[place];
}

Hope this will help you.

What is the fastest way to calculate a Windows folders size?

There is no simple way to do this in .Net; you will have to loop through every file and subdir.
See the examples here to see how it's done.



Related Topics



Leave a reply



Submit