Sometimes you need to enumerate the files in a folder and its subfolders and that can be a very long task, especially with large folders. This article will show a fast and easy way to enumerate all files in a folder when using UWP.
Enumerating files the traditional way
UWP runs in a sandbox and you don't have access to all files in the system. One way to get access to a folder and its subfolders is to use the FolderPicker. You can't access the folders directly (unless for some libraries when you ask for their use in the app manifest), and using the folder picker gives you access to other folders: you open it, the user selects a folder and you have access to it and its subfolders. That can be done with a code like this:
private static async Task<StorageFolder> SelectFolderAsync()
{
var folderPicker = new Windows.Storage.Pickers.FolderPicker
{
SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.Desktop
};
folderPicker.FileTypeFilter.Add("*");
StorageFolder folder = await folderPicker.PickSingleFolderAsync();
return folder;
}
Once you get the folder reference, you can enumerate all files using a code like this:
private List<StorageFile> _allFiles;
private async Task GetFilesInFolder(StorageFolder folder)
{
var items = await folder.GetItemsAsync();
foreach (var item in items)
{
if (item is StorageFile)
_allFiles.Add(item as StorageFile);
else
await GetFilesInFolder(item as StorageFolder);
}
}
This recursive function will get all files and folders in the folder and will add the files to the files list and will call the function for all subfolders. Notice that you must use the StorageFolder and StorageFile classes to enumerate the files. While you can use .NET Standard 2.0 FileIO classes, this will be blocked by access permissions to UWP programs (for example, you will have access denied on the Documents library if you try to enumerate any file there with these classes even if you got the folder through the FolderPicker).
Using this function, I can get all files in a folder and fill a GridView in the main page with this function:
private async void GetFolderSlow(object sender, RoutedEventArgs e)
{
StorageFolder folder = await SelectFolderAsync();
if (folder != null)
{
DocsGrid.ItemsSource = null;
StatusTxt.Text = "";
var sw = new Stopwatch();
sw.Start();
_allFiles = new List<StorageFile>();
await GetFilesInFolder(folder);
DocsGrid.ItemsSource = _allFiles;
sw.Stop();
StatusTxt.Text = $"Ellapsed time: {sw.ElapsedMilliseconds} {_allFiles.Count} files";
}
}
In my machine, it takes about 2 minutes to enumerate the Documents Library (about 38,000 files). That is way more than using a .NET console program (in my machine, it took about 5 seconds to get all file names and process them to get their sizes), but there is no better way (until now - maybe there will be some kind of Full Thrust UWP app in the future 😃) to enumerate the files in UWP. Or there is?
Enumerating files the fast way
Yes, there is a faster way. You can use the Windows indexing service and query the files faster, when they are indexed. For that, you just need to create a QueryOptions instance and tell that you want to use the indexer. This code makes all the work:
private async void GetFolder(object sender, RoutedEventArgs e)
{
var folder = await SelectFolderAsync();
if (folder != null)
{
DocsGrid.ItemsSource = null;
StatusTxt.Text = "";
var sw = new Stopwatch();
sw.Start();
var queryOptions = new QueryOptions
{
FolderDepth = FolderDepth.Deep,
IndexerOption = IndexerOption.UseIndexerWhenAvailable
};
var query = folder.CreateFileQueryWithOptions(queryOptions);
var allFiles = await query.GetFilesAsync();
DocsGrid.ItemsSource = allFiles;
sw.Stop();
StatusTxt.Text = $"Ellapsed time: {sw.ElapsedMilliseconds} {allFiles.Count} files";
}
}
As you can see, there is no need to have a recursive function. Setting the FolderDepth property to FolderDepth.Deep does all the recursive work for you. An you can use the index if it's available and query for some files (like - only pictures or files created by an author). While this is not as fast as the console enumeration, it's still twice as fast as the previous version.
Conclusions
Enumerating files in UWP is not very fast, but when you need it, you have a faster alternative to the default, using the indexer. That can be a handy alternative, especially when you need to filter the files.
All the source code for the article is in https://github.com/bsonnino/DocumentsAccess
Hi, I just wanted to mention that using getItemsAsync can’t really be used on large enumeration irrespective of speed as the file broker of the sandbox will kill the memory. When you got about 4,000 files it’ll hit 2gb memory and start to impact other apps. At 8,000 files you’ll probably hit out of memory errors.
The indexer is good, but it has two drawbacks, it doesn’t allow you feedback to the UI any progress and it has no information about the files, you have to query each one to get the basics.
So far I’ve not found anyway to eneumerate quickly using uwp.
Soooooo useful! Many thanks!
Fantastic, thank you.
I spent quite a time dealing with GetFilesAsync and GetFoldersAsync of StorageFolder, these two had really weird behavior (not executing!) and I was disappointed about this new methods until I saw your solution.
If I am not mistaken, all these operations have massive memory leaks. I have posted about them here:
https://docs.microsoft.com/en-us/answers/questions/112585/uwpcwinrt-get-causing-memory-leaks-with-async-oper.html