ASP.NET Core应用中使用得最多的还是具体的物理文件,比如配置文件、View文件以及作为Web资源的静态文件。物理文件系统由定义在NuGet包“Microsoft.Extensions.FileProviders.Physical”中的PhysicalFileProvider来构建。我们知道System.IO命名空间下定义了一整套针操作物理目录和文件的API,实际上PhysicalFileProvider最终也是通过调用这些API来完成相关的IO操作。

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();
}

一、PhysicalFileInfo

一个PhysicalFileProvider对象总是映射到某个具体的物理目录上,被映射的目录所在的路径通过构造函数的参数root来提供,该目录将作为PhysicalFileProvider的根目录。GetFileInfo方法返回的IFileInfo对象代表指定路径对应的文件,这是一个类型为PhysicalFileInfo的对象。一个物理文件可以通过一个System.IO.FileInfo对象来表示,一个PhysicalFileInfo对象实际上就是对该对象的封装,定义在PhysicalFileInfo的所有属性都来源于这个FileInfo对象。对于创建读取文件输出流的CreateReadStream方法来说,它返回的是一个根据物理文件绝对路径创建的FileStream对象。

public class PhysicalFileInfo : IFileInfo
{
...
public PhysicalFileInfo(FileInfo info);
}

对于PhysicalFileProvider的GetFileInfo方法来说,即使我们指定的路径指向一个具体的物理文件,它并不总是会返回一个PhysicalFileInfo对象。PhysicalFileProvider会将一些场景视为“目标文件不存在”,并让GetFileInfo方法返回一个NotFoundFileInfo对象。具体来说,PhysicalFileProvider的GetFileInfo方法在如下的场景中会返回一个NotFoundFileInfo对象:

  • 确实没有一个物理文件与指定的路径相匹配。
  • 如果指定的是一个绝对路径(比如“c:\foobar”),即Path.IsPathRooted方法返回True。
  • 如果指定的路径指向一个隐藏文件。

顾名思义,具有如下定义的NotFoundFileInfo类型表示一个“不存在”的文件。NotFoundFileInfo对象的Exists属性总是返回False,而其他的属性则变得没有任何意义。当我们调用它的CreateReadStream试图读取一个根本不存在的文件内容时,会抛出一个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.");
}

二、PhysicalDirectoryInfo

PhysicalFileProvider利用一个PhysicalFileInfo对象来描述某个具体的物理文件,而一个物理目录则通过一个PhysicalDirectoryInfo的对象来描述。既然PhysicalFileInfo是对一个FileInfo对象的封装,那么我们应该想得到PhysicalDirectoryInfo对象封装的就是表示目录的DirectoryInfo对象。如下面的代码片段所示,我们需要在创建一个PhysicalDirectoryInfo对象时提供这个DirectoryInfo对象,PhysicalDirectoryInfo实现的所有属性的返回值都来源于这个DirectoryInfo对象。由于CreateReadStream方法的目的总是读取文件的内容,所以PhysicalDirectoryInfo类型的这个方法会抛出一个InvalidOperationException类型的异常。

public class PhysicalDirectoryInfo : IFileInfo
{
...
public PhysicalDirectoryInfo(DirectoryInfo info);
}

三、PhysicalDirectoryContents

当我们调用PhysicalFileProvider的GetDirectoryContents方法时,如果指定的路径指向一个具体的目录,那么该方法会返回一个类型为PhysicalDirectoryContents的对象。PhysicalDirectoryContents是一个IFileInfo对象的集合,该集合中包括所有描述子目录的PhysicalDirectoryInfo对象和描述文件的PhysicalFileInfo对象。PhysicalDirectoryContents的Exists属性取决于指定的目录是否存在。

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

四、NotFoundDirectoryContents

如果指定的路径并不指向一个存在的目录,或者指定的是一个绝对路径,GetDirectoryContents方法都会返回一个Exsits为False的NotFoundDirectoryContents对象。如下所示的代码片段展示了NotFoundDirectoryContents类型的定义,如果我们需要使用到这么一个类型,可以直接利用静态属性Singleton得到对应的单例对象。

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();
}

五、PhysicalFilesWatcher

我们接着来谈谈PhysicalFileProvider的Watch方法。当我们调用该方法的时候,PhysicalFileProvider会通过解析我们提供的Globbing Pattern表达式来确定我们期望监控的文件或者目录,并最终利用FileSystemWatcher对象来对这些文件实施监控。这些文件或者目录的变化(创建、修改、重命名和删除等)都会实时地反映到Watch方法返回的IChangeToken上。

PhysicalFileProvider的Watch方法中指定的Globbing Pattern表达式必须是针对当前根目录的相对路径,我们可以使用“/”或者“./”前缀,也可以不采用任何前缀。一旦我们使用了绝对路径(比如“c:\test\*.txt”)或者“../”前缀(比如“../test/*.txt”),不论解析出来的文件是否存在于PhysicalFileProvider的根目录下,这些文件都不会被监控。除此之外,如果我们没有指定Globbing Pattern表达式,PhysicalFileProvider也不会有任何的文件会被监控。

PhysicalFileProvider针对物理文件系统变化的监控是通过如下这个PhysicalFilesWatcher对象实现的,其Watch方法内部会直接调用PhysicalFileProvider的CreateFileChangeToken方法,并返回得到的IChangeToken对象。这是一个公共类型,如果我们具有监控物理文件系统变化的需要,可以直接使用这个类型。

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

从PhysicalFilesWatcher构造函数的定义我们不难看出,它最终是利用一个FileSystemWatcher对象(对应于fileSystemWatcher参数)来完成针对指定根目录下(对应于root参数)所有子目录和文件的监控。FileSystemWatcher的CreateFileChangeToken方法返回的IChangeToken对象会帮助我们感知到子目录或者文件的添加、删除、修改和重命名,但是它会忽略隐藏的目录和文件。最后需要提醒的是,当我们不再需要对指定目录实施监控的时候,记得调用PhysicalFileProvider的Dispose方法,该方法会负责将FileSystemWatcher对象关闭。

六、小结

我们借助下图所示的UML来对由PhysicalFileProvider构建物理文件系统的整体设计做一个简单的总结。首先,该文件系统使用PhysicalDirectoryInfo和PhysicalFileInfo对类型来描述目录和文件,它们分别是对DirectoryInfo和FileInfo(System.IO.FileInfo)对象的封装。

PhysicalFileProvider的GetDirectoryContents方法返回一个PhysicalDirectoryContents 对象(如果指定的目录存在),组成该对象的分别是根据其所有子目录和文件创建的PhysicalDirectoryInfo和PhysicalFileInfo对象。当我们调用PhysicalFileProvider的GetFileInfo方法时,如果指定的文件存在,返回的是描述该文件的PhysicalFileInfo对象。至于PhysicalFileProvider的Watch方法,它最终利用了FileSystemWatcher来监控指定文件或者目录的变化。

[ASP.NET Core 3框架揭秘] 文件系统[1]:抽象的“文件系统”
[ASP.NET Core 3框架揭秘] 文件系统[2]:总体设计
[ASP.NET Core 3框架揭秘] 文件系统[3]:物理文件系统
[ASP.NET Core 3框架揭秘] 文件系统[4]:程序集内嵌文件系统

[ASP.NET Core 3框架揭秘] 文件系统[3]:物理文件系统的更多相关文章

  1. [ASP.NET Core 3框架揭秘] 文件系统[1]:抽象的“文件系统”

    ASP.NET Core应用 具有很多读取文件的场景,比如配置文件.静态Web资源文件(比如CSS.JavaScript和图片文件等)以及MVC应用的View文件,甚至是直接编译到程序集中的内嵌资源文 ...

  2. [ASP.NET Core 3框架揭秘] 文件系统[2]:总体设计

    在<抽象的"文件系统">中,我们通过几个简单的实例演示从编程的角度对文件系统做了初步的体验,接下来我们继续从设计的角度来进一步认识它.这个抽象的文件系统以目录的形式来组 ...

  3. [ASP.NET Core 3框架揭秘] 文件系统[4]:程序集内嵌文件系统

    一个物理文件可以直接作为资源内嵌到编译生成的程序集中.借助于EmbeddedFileProvider,我们可以采用统一的编程方式来读取内嵌的资源文件,该类型定义在 "Microsoft.Ex ...

  4. ASP.NET Core 6框架揭秘实例演示[07]:文件系统

    ASP.NET Core应用具有很多读取文件的场景,如读取配置文件.静态Web资源文件(如CSS.JavaScript和图片文件等).MVC应用的视图文件,以及直接编译到程序集中的内嵌资源文件.这些文 ...

  5. [ASP.NET Core 3框架揭秘] 依赖注入:控制反转

    ASP.NET Core框架建立在一些核心的基础框架之上,这些基础框架包括依赖注入.文件系统.配置选项和诊断日志等.这些框架不仅仅是支撑ASP.NET Core框架的基础,我们在进行应用开发的时候同样 ...

  6. [ASP.NET Core 3框架揭秘] 配置[7]:多样化的配置源[中篇]

    物理文件是我们最常用到的原始配置载体,而最佳的配置文件格式主要有三种,它们分别是JSON.XML和INI,对应的配置源类型分别是JsonConfigurationSource.XmlConfigura ...

  7. [ASP.NET Core 3框架揭秘] 跨平台开发体验: Linux

    如果想体验Linux环境下开发.NET Core应用,我们有多种选择.一种就是在一台物理机上安装原生的Linux,我们可以根据自身的喜好选择某种Linux Distribution,目前来说像RHEL ...

  8. 《ASP.NET Core 3框架揭秘》读者群,欢迎加入

    作为一个17年的.NET开发者,我对一件事特别不能理解:我们的计算机图书市场充斥着一系列介绍ASP.NET Web Forms.ASP.NET MVC.ASP.NET Web API的书籍,但是却找不 ...

  9. 《ASP.NET Core 3框架揭秘》博文汇总

    在过去一段时间内,写了一系列关于ASP.NET Core 3相关的文章,其中绝大部分来源于即将出版的<ASP.NET Core 3框架揭秘>(博文只能算是"初稿",与书 ...

随机推荐

  1. cognos服务器性能测试诊断分析优化过程记录

    前段时间客户方一个系统上线后出现性能问题,就是查询报表的时候出现宕机现象,应项目组要求过去帮忙测试优化问题.  该项目的架构相对比较复杂,登录后要先进行认证服务器认证用户然后登录到应用系统A,在跳转到 ...

  2. python 实现图片批量加入水印!pillow 入门实战!

    写文章的时候可以设置是否添加水印.可是,有些图片可能想加水印,有些不想加水印,该怎么办呢? 配置环境 python3 + pillow pip3 install pillow 引入库 from PIL ...

  3. Css搭建

    教你做css比较好的网站: https://www.jianshu.com/p/23b2bfc9a90d?tdsourcetag=s_pcqq_aiomsg https://cloud.tencent ...

  4. 空气质量管理系统+SSM(Spring+SpringMVC+Mybatis)+前后端分离总结

    作者:故事我忘了¢个人微信公众号:程序猿的月光宝盒 点我进SSM演示地址 1.目录结构: 2.需要注意的地方 2.1在WEB-INFO下新建 2.1.1 springMVC-servlet.xml & ...

  5. Kotlin实战案例:带你实现RecyclerView分页查询功能(仿照主流电商APP,可切换列表和网格效果)

    随着Kotlin的推广,一些国内公司的安卓项目开发,已经从Java完全切成Kotlin了.虽然Kotlin在各类编程语言中的排名比较靠后(据TIOBE发布了 19 年 8 月份的编程语言排行榜,Kot ...

  6. golang实现rabbitmq消息队列失败尝试

    在工作中发现,有些时候消息因为某些原因在消费一次后,如果消息失败,这时候不ack,消息就回一直重回队列首部,造成消息拥堵. 如是有了如下思路: 消息进入队列前,header默认有参数 retry_nu ...

  7. java静态初始化块(静态域)

    1. 类变量的初始化可通过静态初始化块来进行. 代码放在一对大括号内,大括号前用static修饰:static {……} 一个类可定义1个或多个静态初始化块. 静态初始化块会在加载时调用而且只被调用一 ...

  8. wdCP v3.3.8apache阿里云ssl证书实现智慧软文http转换https的详细操作教程

    先展示一下效果:智慧软文发布系统(https://www.zhihuiruanwen.com) 之前用的是传统的http,发现360浏览器,火狐浏览器,谷歌浏览器均提示不安全的链接,最主要的是第一次打 ...

  9. PHP常用字符串函数总结

    PHP语言中的字符串函数也是一个比较易懂的知识.今天我们就为大家总结了将近12种PHP字符串函数,希望对又需要的朋友有所帮助,增加读者朋友的PHP知识库. 1.查找字符位置函数 strpos($str ...

  10. su和sudo的区别与使用

    一.   使用 su 命令临时切换用户身份 1.su 的适用条件和威力 su命令就是切换用户的工具,怎么理解呢?比如我们以普通用户beinan登录的,但要添加用户任务,执行useradd ,beina ...