Physical file system built by Physicalfileprovider
The most common use in ASP. NET core applications is specific physical files, such as configuration files, view files, and static files on Web pages, and the abstraction of a physical file system is achieved by Physicalfileprovider this fileprovider. The type is defined in the NuGet package "Microsoft.Extensions.FileProviders.Physical". We know that the API that defines a full set of pins to manipulate physical directories and files under the System.IO namespace is, in fact, ultimately physicalfileprovider by invoking these APIs to do the related IO operations. [This article has been synced to the "ASP. NET Core Framework"]
Directory
First, Physicalfileprovider
Second, PhysicalFileInfo
Third, Physicaldirectoryinfo
Iv. Monitoring of physical files
V. Summary
First, Physicalfileprovider
The code snippet shown below shows the definition of the Physicalfileprovider type.
1:public class Physicalfileprovider:ifileprovider, IDisposable
2: {
3: Public Physicalfileprovider (string root);
4:
5: Public ifileinfo GetFileInfo (string subpath);
6:
7: Public ichangetoken Watch (string filter);
9: Public void Dispose ();
10:}
Second, PhysicalFileInfo
A Physicalfileprovider object is always mapped to a specific physical directory, and the path to the mapped directory is provided by the constructor's parameter root, which will act as the root directory of the Physicalfileprovider. The FileInfo object returned by the GetFileInfo method represents the file corresponding to the specified path, which is an object of type PhysicalFileInfo, and the code snippet shown below shows the full definition of the type. A physical file can be represented by a System.IO.FileInfo object, and a PhysicalFileInfo object is actually an encapsulation of this FileInfo object, All attributes defined in PhysicalFileInfo are derived from this FileInfo object. For the Createreadstream method that creates the read file output stream, it returns a FileStream object created from the absolute path of the physical file.
1:public class Physicalfileinfo:ifileinfo
2: {
3: ...
4: Public physicalfileinfo (FileInfo info);
5:}
For Physicalfileprovider's GetFile method, even if the path we specify points to a specific physical file, it does not always return a PhysicalFileInfo object . Specifically, Physicalfileprovider will consider the following scenarios as "The destination file does not exist" and let GetFile return a Notfoundfileinfo object. As the name implies, Notfoundfileinfo represents the formal "nonexistent" file, that is, its exists property always returns false, while the other attributes become meaningless. When we call its createreadstream to try to read a file that does not exist at all, it throws an exception of type FileNotFoundException.
- There is really not a physical file that matches the specified path.
- If you specify an absolute path (such as "C:\foobar"), that is, path.ispathrooted returns True.
- If the specified path points to a hidden file.
Third, Physicaldirectoryinfo
For Physicalfileprovider, it uses the PhysicalFileInfo object to describe a specific physical file, and the description of the directory is through an object of type Physicaldirectoryinfo. Since PhysicalFileInfo is the encapsulation of a System.IO.FileInfo object, we should want to think that the nature of physicaldirectoryinfo encapsulation is the DirectoryInfo object that represents the 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 the properties implemented by Physicaldirectoryinfo are derived from this DirectoryInfo object. Since the purpose of the Createreadstream method is to read the contents of a file, when we invoke this method of a Physicaldirectoryinfo object, we throw an exception of type InvalidOperationException.
1:public class Physicaldirectoryinfo:ifileinfo
2: {
3: ...
4: Public physicaldirectoryinfo (DirectoryInfo info);
5:}
When we call Physicalfileprovider's Getdirectorycontents method, the method returns an object of type Enumerabledirectorycontents if the specified path points to a specific directory , but Enumerabledirectorycontents is only an internal type that is not visible during the programming process. Enumerabledirectorycontents is a collection of FileInfo objects that include all PhysicalFileInfo objects that describe the Physicaldirectoryinfo object and the description file of the subdirectory. As for Enumerabledirectorycontents's exists property, it always returns TRUE. If the specified path does not point to an existing directory, or if an absolute path is specified, this method returns a Notfounddirectorycontents object that always returns false for the Exsits property.
iv. monitoring of physical files
Let's talk about Physicalfileprovider's Watch method. When we call this method, Physicalfileprovider will determine the files we expect to monitor by parsing the filter expressions we provide, and then use the FileSystemWatcher object to try to monitor the files. Changes to these files (create, modify, rename, and delete) are reflected in real time to the Changetoken returned by the watch method. It is worth mentioning that the FileSystemWatcher type implements the IDisposable interface, Physicalfileprovider also implements the same interface, The only mission of the Physicalfileprovider Dispose method is to release the FileSystemWatcher object .
The filter expression specified in the Watch method must be a relative path to the current Physicalfileprovider root directory, either with a "/" or "./" prefix, or without any prefix. Once we have used an absolute path (such as "c:\test\*.txt") or ". /"prefix (e.g."). /test/*.txt "), these files are not monitored, regardless of whether the parsed files exist in the root directory of the Physicalfileprovider. In addition, if we do not specify any filter criteria, no files will be monitored.
The real purpose of monitoring file changes is to enable the application to be aware of changes in the data source in a timely manner, thereby automating some pre-registered rollback operations. The registration of the callback can be done directly by calling Changetoken's Registerchangecallback method, and the registered callback is represented by a delegate object of type action<object>. For the example of file monitoring demonstrated in the first section, the corresponding program "supposedly" can be changed into the following form.
1:ifileprovider Fileprovider = new Physicalfileprovider (@ "c:\test");
2:fileprovider.watch ("Data.txt"). Registerchangecallback (_ = >loadfileasync (Fileprovider), null);
3:while (True)
4: {
5: File.writealltext (@ "C:\test\data.txt", DateTime.Now.ToString ());
6: task.delay (5000). Wait ();
7:}
9:public static async void Loadfileasync (Ifileprovider fileprovider)
10: {
One: stream stream = Fileprovider.getfileinfo ("Data.txt"). Createreadstream ();
: {
: byte[] buffer = new Byte[stream. Length];
: await stream. Readasync (buffer, 0, buffer.) Length);
: Console.WriteLine (Encoding.ASCII.GetString (buffer));
: }
17:}
If we execute the above program, we will find that only the first update to the file can be sensed , and subsequent file update operations will be ignored automatically. The root cause of this problem is that the mission of a single Changetoken object is to send a corresponding signal when the bound data source transforms for the first time, without the ability to continuously transmit data transformations. In fact, this can be seen from the definition of the Ichangetoken interface, and we know that it has a haschanged property that indicates whether the data has changed, and does not provide a way to "reset" the property. So when we need to monitor a file continuously, we need to re-invoke the Fileprovider watch method in the registered callback and register the callback again with the build Changetoken. In addition, consider the Changetoken Registerchangecallback method to return the callback registration object in the form of a IDisposable object, We should release the Dispose method of the callback registration object that was returned the first time when the callback was implemented two times. The program shown below will be able to achieve the purpose of continuous monitoring of the file test.
1:ifileprovider Fileprovider = new Physicalfileprovider (@ "c:\test");
2:action<object> callback = null;
3:idisposable regiser = null;
4:callback = _ = =
5: {
6: Regiser. Dispose ();
7: Loadfileasync (Fileprovider);
8: fileprovider.watch ("Data.txt"). Registerchangecallback (callback, NULL);
9:};
11:regiser = Fileprovider.watch ("Data.txt"). Registerchangecallback (callback, NULL);
However, this kind of programming method not only looks more cumbersome, many people who lack knowledge of changetoken even to this kind of programming way incomprehensible. To solve this problem, we can use the following two methods defined in the Changetoken type onchange method to register the callback that is executed automatically when the data changes. The two methods have two parameters, the former is a func<ichangetoken> object used to create the Changetoken object, and the latter is the action<object>/action<tstate that represents the callback operation. > Objects. In fact, in the first section of the example, we are calling this onchange method.
1:public Static Class Changetoken
2: {
3: Public static IDisposable OnChange (func<ichangetoken> changetokenproducer, Action Changetokenconsumer)
4: {
5: action<object> callback = null;
6: callback = Delegate (object s) {
7: Changetokenconsumer ();
8: changetokenproducer (). Registerchangecallback (callback, NULL);
9: };
: return Changetokenproducer (). Registerchangecallback (callback, NULL);
One: }
: Public static IDisposable onchange<tstate> (func<ichangetoken> changetokenproducer, action< Tstate> Changetokenconsumer, Tstate State)
: {
: action<object> callback = null;
: callback = Delegate (object s) {
: Changetokenconsumer ((tstate) s);
: changetokenproducer (). Registerchangecallback (callback, s);
: };
: return Changetokenproducer (). Registerchangecallback (callback, state);
: }
22:}
If you use this onchange method to replace the original manual call Changetoken Registerchangecallback method for callback registration, the original is relatively cumbersome program can be replaced by the following two lines of code. In fact, in "Read and monitor file changes," We call this onchange method.
1:ifileprovider Fileprovider = new Physicalfileprovider (@ "c:\test");
2:changetoken.onchange (() = Fileprovider.watch ("Data.txt"), () = Loadfileasync (Fileprovider));
V. Summary
We use the UML shown to make a simple summary of the overall design of the physical file system built by Physicalfileprovider. First, the file system for describing directories and files is a physicaldirectoryinfo and PhysicalFileInfo object, respectively, They are the encapsulation of a DirectoryInfo and FileInfo (System.IO.FileInfo) object, respectively. The Physicalfileprovider getdirectorycontents method returns a Enumerabledirectorycontents object if the specified directory exists. The objects that make up this object are the Physicaldirectoryinfo and PhysicalFileInfo objects that are created from all of their subdirectories and files. When we call Physicalfileprovider's GetFileInfo method, if the specified file exists, the PhysicalFileInfo object that describes the file is returned. As for the Physicalfileprovider watch method, it eventually uses the FileSystemWatcher to monitor changes to the specified file.
Physical file system built by Physicalfileprovider