The most commonly used ASP.NET Core applications are specific physical files, such as configuration files, View files, and static files as Web resources. The physical file system is constructed by a PhysicalFileProvider defined in NuGet package ” Microsoft.Extensions.FileProviders.Physical “. We know that the System.IO namespace defines a complete set of APIs for manipulating physical directories and files. In fact, PhysicalFileProvider also finally calls these APIs to complete related IO operations.



public  class PhysicalFileProvider: IFileProvider, IDisposable
    public PhysicalFileProvider ( string root);   
    public IFileInfo GetFileInfo ( string subpath);  
     public IDirectoryContents GetDirectoryContents ( string subpath); 
     public IChangeToken Watch ( string filter);

    public  void Dispose ();   



First, PhysicalFileInfo

A PhysicalFileProvider object is always mapped to a specific physical directory. The path of the mapped directory is provided by the root parameter of the constructor. This directory will be used as the root directory of the PhysicalFileProvider. The IFileInfo object returned by the GetFileInfo method represents the file corresponding to the specified path. This is an object of type PhysicalFileInfo . A physical file can be represented by a System.IO.FileInfo object. A PhysicalFileInfo object is actually an encapsulation of this object. All properties defined in PhysicalFileInfo are derived from this FileInfo object. For the CreateReadStream method that creates a read file output stream, it returns a FileStream object created based on the absolute path of the physical file.

public  class PhysicalFileInfo: IFileInfo
    public PhysicalFileInfo (FileInfo info);    

For the GetFileInfo method of PhysicalFileProvider, even if the path we specify points to a specific physical file, it does not always return a PhysicalFileInfo object. PhysicalFileProvider will treat some scenarios as “the target file does not exist ” and let the GetFileInfo method return a NotFoundFileInfo object. Specifically, the GetFileInfo method of PhysicalFileProvider returns a NotFoundFileInfo object in the following scenarios:

  • There is really no physical file that matches the specified path.
  • If an absolute path is specified (such as “c: \ foobar”), the Path.IsPathRooted method returns True.
  • If the specified path points to a hidden file.

As the name implies, the NotFoundFileInfo type defined below represents a “non-existent” file. The Exists property of the NotFoundFileInfo object always returns False, while other properties become meaningless. When we call its CreateReadStream to try to read the contents of a file that does not exist at all, it will throw an exception of type FileNotFoundException.



public  class NotFoundFileInfo: IFileInfo
    public  bool Exists => false ;   
     public  long Length => throw  new NotImplementedException ();   
     public  string PhysicalPath => null ;  
     public  string Name { get ;}   
     public DateTimeOffset LastModified => DateTimeOffset.MinValue;
     public  bool IsDirectory => false ;

    public NotFoundFileInfo ( string name) => this .Name = name;
     public Stream CreateReadStream () => throw  new FileNotFoundException ($ " The file {Name} does not exist. " );



Second, PhysicalDirectoryInfo

PhysicalFileProvider uses a PhysicalFileInfo object to describe a specific physical file, and a physical directory is described by a PhysicalDirectoryInfo object. Since PhysicalFileInfo is an encapsulation of a FileInfo object, we should want to get a PhysicalDirectoryInfo object that encapsulates a DirectoryInfo object that represents a directory . As shown in the following code snippet, we need to provide this DirectoryInfo object when creating a PhysicalDirectoryInfo object. The return values ​​of all properties implemented by PhysicalDirectoryInfo are derived from this DirectoryInfo object. Because the purpose of the CreateReadStream method is always to read the contents of the file, this method of type PhysicalDirectoryInfo will throw an InvalidOperationException.

public  class PhysicalDirectoryInfo: IFileInfo
    public PhysicalDirectoryInfo (DirectoryInfo info);

Third, PhysicalDirectoryContents

When we call the GetDirectoryContents method of PhysicalFileProvider, if the specified path points to a specific directory, then this method returns an object of type PhysicalDirectoryContents. PhysicalDirectoryContents is a collection of objects IFileInfo , the set includes all of the subdirectories described PhysicalDirectoryInfo objects describing the file PhysicalFileInfo objects. The Exists property of PhysicalDirectoryContents depends on whether the specified directory exists.



public  class PhysicalDirectoryContents: IDirectoryContents
    public  bool Exists { get ;}
     public PhysicalDirectoryContents ( string directory);
     public IEnumerator <IFileInfo> GetEnumerator ();
    IEnumerator IEnumerable.GetEnumerator ();



Fourth, NotFoundDirectoryContents

If the specified path does not point to an existing directory, or if an absolute path is specified, the GetDirectoryContents method will return a NotFoundDirectoryContents object whose Exsits is False. The following code snippet shows the definition of the NotFoundDirectoryContents type. If we need to use such a type, we can directly use the static property Singleton to get the corresponding singleton object.



public  class NotFoundDirectoryContents: IDirectoryContents
    public  static NotFoundDirectoryContents Singleton { get ;} = new NotFoundDirectoryContents ();
     public  bool Exists => false ;
     public IEnumerator <IFileInfo> GetEnumerator () => Enumerable.Empty <IFileInfo> () .GetEnumerator ();
    IEnumerator IEnumerable.GetEnumerator () => GetEnumerator ();



Five, PhysicalFilesWatcher

Let’s talk about the Watch method of PhysicalFileProvider. When we call this method, the PhysicalFileProvider will parse the Globbing Pattern expression we provide to determine the files or directories we want to monitor, and finally use the FileSystemWatcher object to monitor these files. Changes to these files or directories (create, modify, rename, delete, etc.) are reflected in real time on the IChangeToken returned by the Watch method.

The Globbing Pattern expression specified in the Watch method of the PhysicalFileProvider must be a relative path to the current root directory. We can use the “/” or “./” prefix or not use any prefix. Once we use an absolute path (such as “c: \ test \ *. Txt”) or a “../” prefix (such as “../test/*.txt”), regardless of whether the parsed file exists in the PhysicalFileProvider In the root directory, these files will not be monitored. In addition, if we do not specify the Globbing Pattern expression, the PhysicalFileProvider will not monitor any files.

PhysicalFileProvider monitors the changes of the physical file system through the following PhysicalFilesWatcher object. The Watch method of the PhysicalFileProvider directly calls the CreateFileChangeToken method of the PhysicalFileProvider and returns the obtained IChangeToken object. This is a public type. If we have the need to monitor changes in the physical file system, we can use this type directly.

public  class PhysicalFilesWatcher: IDisposable
    public PhysicalFilesWatcher ( string root, FileSystemWatcher fileSystemWatcher, bool pollForChanges);
     public IChangeToken CreateFileChangeToken ( string filter);
     public  void Dispose ();

From the definition of the PhysicalFilesWatcher constructor, it is not difficult to see that it finally uses a FileSystemWatcher object (corresponding to the fileSystemWatcher parameter) to complete the monitoring of all subdirectories and files under the specified root directory (corresponding to the root parameter). The IChangeToken object returned by the FileSystemWatcher’s CreateFileChangeToken method will help us perceive the addition, deletion, modification, and renaming of subdirectories or files, but it will ignore hidden directories and files. The last thing to remind is that when we no longer need to monitor the specified directory, remember to call the Dispose method of PhysicalFileProvider, which will be responsible for closing the FileSystemWatcher object.


We use the UML shown in the figure below to make a simple summary of the overall design of the physical file system built by PhysicalFileProvider. First, the file system uses the PhysicalDirectoryInfo and PhysicalFileInfo pairs to describe directories and files, which are encapsulations of DirectoryInfo and FileInfo (System.IO.FileInfo) objects, respectively.



The GetDirectoryContents method of PhysicalFileProvider returns a PhysicalDirectoryContents object (if the specified directory exists), which consists of PhysicalDirectoryInfo and PhysicalFileInfo objects created from all its subdirectories and files respectively. When we call the GetFileInfo method of PhysicalFileProvider, if the specified file exists, a PhysicalFileInfo object describing the file is returned. As for the PhysicalFileProvider’s Watch method, it ultimately utilizes FileSystemWatcher to monitor changes to a specified file or directory.



ASP.Net Core3 File system[1] Abstract File System

ASP.Net Core3 File system[2] Overall design

ASP.Net Core 3 File System [3] Physical File System

ASP.Net Core3 File system[4] Embedded File systeme System


Orignal link: