File Providers In ASP.NET Core

The file system can be accessed through the use of File Providers in ASP.NET Core. In other words, we can say that "File Provider is an abstraction over file system". The file providers use IFileProvider interface and it has methods to get file information, directory information, and also, the setup change notification.
The IFileInfo is used to get the file information, and it provides the methods and properties for individual files and directories. The IDirectoryContents interface is used to retrieve directory's content in the file provider. The IChangeToken interface propagates notifications that a change has occurred.
There are three types of IFileProvider Implementations -
  • Physical File Provider - it is used to access the actual or physical file of the system
  • Embedded File Provider - it is used to access files that are embedded in assemblies
  • Composite File Provider - it is used to provide combine access to the files from one or more providers.
The PhysicalFileProvider wraps the System.IO. File type and provides the access to the physical file system. It is able to access all the paths to a directory and sub-directory. It has limited access to some of the directory. We can instantiate a PhysicalFileProvider provider from the ConfigureServices service method of startup class and we can inject it as a dependency in controller constructor.
In the following code, I have created PhysicalFileProvider object and injected as dependency.
ConfigureServices method of Startup Class
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     services.AddMvc();  
  4.     IFileProvider physicalProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());  
  6.     services.AddSingleton<IFileProvider>(physicalProvider);  
  7. }  
  1. using Microsoft.AspNetCore.Mvc;  
  2. using Microsoft.Extensions.FileProviders;  
  4. public class HomeController : Controller  
  5. {  
  6.     private readonly IFileProvider _fileProvider;  
  7.     public HomeController(IFileProvider fileProvider)  
  8.     {  
  9.         _fileProvider = fileProvider;  
  10.     }  
  11.     [Route("home/index")]  
  12.     public IActionResult Index()  
  13.     {  
  14.         var contents = _fileProvider.GetDirectoryContents("");  
  15.         return View(contents);  
  16.     }  
  17. }  
  1. @using Microsoft.Extensions.FileProviders  
  2. @model  IDirectoryContents  
  4. <h2>Folder Contents</h2>  
  6. <ul>  
  7.     @foreach (IFileInfo item in Model)  
  8.     {  
  9.         if (item.IsDirectory)  
  10.         {  
  11.             <li><strong>@item.Name</strong> - Directory</li>  
  12.         }  
  13.         else  
  14.         {  
  15.             <li><strong>@item.Name</strong> - @item.PhysicalPath </li>  
  16.         }  
  17.     }  
  18. </ul>  

This provider is used to access the files that are embedded in assemblies. We can embed files in assembly using "EmbeddedResource" element in .csproj file (with Visual Studio 2017). We can also specify the files using globbing patterns.
Here, we need to create EmbeddedFileProvider object and inject it as dependency. The Controller and View code remain same.
  1. IFileProvider embeddedProvider = new EmbeddedFileProvider(Assembly.GetEntryAssembly());  
  2. services.AddSingleton<IFileProvider>(embeddedProvider);  
It combines two or more IFileProvider instances and exposing a single interface for working with files. In the following example I have created CompositeFileProvider and passed two IFileProvider instances (physical and embedded providers) to its constructor. As a result, we can get both type provider of files.
  1. IFileProvider physicalProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());  
  2. IFileProvider embeddedProvider = new EmbeddedFileProvider(Assembly.GetEntryAssembly());  
  3. IFileProvider compositeProvider = new CompositeFileProvider(physicalProvider, embeddedProvider);  
  5. services.AddSingleton<IFileProvider>(compositeProvider);  

Watching on files or directories change
The Watch method of IFileProvider provides the way to watch one or more directories and files for changes. This method accepts path as string. We can also specify the multiple file or directories path using globbing patterns. This method returns IChangeToken. The IChangeToken exposes "HasChanged" property which used to inspection and RegisterChangeCallback method is called when changes are detected. For continuously monitoring, we need to use TaskCompletionSource.

In the following example, I have created a console application that watches on "watchFil.txt" and console application displays message when this text file is modified.
  1. using System;  
  2. using System.IO;  
  3. using System.Threading.Tasks;  
  4. using Microsoft.Extensions.FileProviders;  
  5. using Microsoft.Extensions.Primitives;  
  7. namespace ConsoleApplication  
  8. {  
  9.     public class Program  
  10.     {  
  11.         private static PhysicalFileProvider _fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());  
  12.         public static void Main(string[] args)  
  13.         {  
  14.             while (true)  
  15.             {  
  16.                 MainAsync().GetAwaiter().GetResult();  
  17.             }  
  18.         }  
  19.         private static async Task MainAsync()  
  20.         {  
  21.             IChangeToken token = _fileProvider.Watch("WatchFile.txt");  
  22.             var source = new TaskCompletionSource<object>();  
  23.             token.RegisterChangeCallback(state =>  
  24.                 ((TaskCompletionSource<object>)state).TrySetResult(null), source);  
  25.             await source.Task.ConfigureAwait(false);  
  26.             Console.WriteLine("File changed - WatchFile.txt");  
  27.         }  
  28.     }  
  29. }  


Using the IFileProvider, we can access the file system in ASP.NET Core application and using file watcher, and we are able to track file changes.