Reputation: 179
The author of this post mentioned there is a newer API for quicker method of file and directory enumeration. But though the article seems to refer to examples, they are not present and I can't find any examples elsewhere on Google that use the new methods mentioned?
Does anyone have some example code that could enumerate a given folder and all sub-folders / files to return information such as size and last accessed etc?
Upvotes: 6
Views: 2624
Reputation: 14630
I can see why you found it difficult to figure this out - there isn't much information around, and so I ended up digging around the source code to figure things out. For the following examples, I'm using this directory structure:
D:\Root\
|- Child\
| |- Grandchild\
| | |- Grandchild.jpg
| | |- Grandchild.txt
| |- Child.html
| |- Child.txt
|- Root.txt
and importing the following namespaces:
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Enumeration;
var enumeration = new FileSystemEnumerable<FileSystemInfo>(
@"D:\Root",
// What's returned here has to match the generic type
// passed in when creating the FileSystemEnumerable.
// As DirectoryInfo inherits from FileSystemInfo, we
// can use FileSystemInfo for both directories and files.
(ref FileSystemEntry entry) => entry.ToFileSystemInfo(),
// Should be self-explanatory.
new EnumerationOptions() { RecurseSubdirectories = true })
{
// We want to include everything: all directories and files.
ShouldIncludePredicate = (ref FileSystemEntry entry) => true
};
As FileSystemEnumerable<T>
implements IEnumerable<T>
, for our example it's a case of iterating over a collection of FileSystemInfo
- there's nothing special here:
foreach (var item in enumeration)
{
Console.WriteLine($"{item.FullName} - {item.LastAccessTime}");
}
which prints:
D:\Root\Child - 03/10/2020 18:34:16
D:\Root\Root.txt - 03/10/2020 17:39:54
D:\Root\Child\Child.html - 03/10/2020 18:27:20
D:\Root\Child\Child.txt - 03/10/2020 17:40:15
D:\Root\Child\Grandchild - 03/10/2020 18:44:36
D:\Root\Child\Grandchild\Grandchild.jpg - 03/10/2020 18:44:28
D:\Root\Child\Grandchild\Grandchild.txt - 03/10/2020 17:40:10
You'll notice a couple of things:
// We're returning strings this time.
var enumeration = new FileSystemEnumerable<string>(
@"D:\Root",
// Returns the full path to the directory.
(ref FileSystemEntry entry) => entry.ToFullPath(),
new EnumerationOptions() { RecurseSubdirectories = true })
{
// Only include directories in the result set.
ShouldIncludePredicate = (ref FileSystemEntry entry) => entry.IsDirectory
};
Running this:
foreach (var item in enumeration)
{
Console.WriteLine(item);
}
prints:
D:\Root\Child
D:\Root\Child\Grandchild
var extensions = new List<string> { ".html", ".jpg" };
// Back to using FileSystemInfo.
var enumeration = new FileSystemEnumerable<FileSystemInfo>(
@"D:\Root",
(ref FileSystemEntry entry) => entry.ToFileSystemInfo(),
new EnumerationOptions() { RecurseSubdirectories = true })
{
ShouldIncludePredicate = (ref FileSystemEntry entry) =>
{
// Skip directories.
if (entry.IsDirectory)
{
return false;
}
foreach (string extension in extensions)
{
var fileExtension = Path.GetExtension(entry.FileName);
if (fileExtension.EndsWith(extension, StringComparison.OrdinalIgnoreCase))
{
// Include the file if it matches one of our extensions.
return true;
}
}
// Doesn't match, so exclude it.
return false;
}
};
Running this:
foreach (var item in enumeration)
{
Console.WriteLine(item.FullName);
}
prints:
D:\Root\Child\Child.html
D:\Root\Child\Grandchild\Grandchild.jpg
I just found another delegate, which controls the recursive behaviour. Let's say we modify the original directory structure to include a new directory with files that we want to ignore:
D:\Root\
|- Child\
| |- Grandchild\
| | |- Grandchild.jpg
| | |- Grandchild.txt
| |- GrandchildIgnore\
| | |- GrandchildIgnore.txt
| |- Child.html
| |- Child.txt
|- Root.txt
To get a list of all the other files, whilst ignoring files in our new directory, we can set the ShouldRecursePredicate
:
var enumeration = new FileSystemEnumerable<FileSystemInfo>(
@"D:\Root",
(ref FileSystemEntry entry) => entry.ToFileSystemInfo(),
new EnumerationOptions() { RecurseSubdirectories = true })
{
ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory,
// If the directory's name ends with ignore, skip it.
ShouldRecursePredicate = (ref FileSystemEntry entry) => !entry.ToFullPath().EndsWith("Ignore", StringComparison.OrdinalIgnoreCase))
};
Running this:
foreach (var item in enumeration)
{
Console.WriteLine(item.FullName);
}
prints:
D:\Root\Root.txt
D:\Root\Child\Child.html
D:\Root\Child\Child.txt
D:\Root\Child\Grandchild\Grandchild.jpg
D:\Root\Child\Grandchild\Grandchild.txt
excluding the directory, as we wanted.
var enumeration = new FileSystemEnumerable<(long, string)>(
@"D:\Root",
(ref FileSystemEntry entry) => (entry.Length, entry.FileName.ToString()))
{
ShouldIncludePredicate = (ref FileSystemEntry entry) => !entry.IsDirectory
};
Given 1 file in D:\Root
that has 122 bytes, running this:
foreach (var (length, fileName) in enumeration)
{
Console.WriteLine($"Name: {fileName} Length: {length}");
}
prints:
Name: Root.txt Length: 122
Upvotes: 10