前言

在开发过程中,肯定避免不了读取文件操作,比如读取配置文件、上传和下载文件、Web中html、js、css、图片等静态资源的访问;在配置文件读取章节中有说到,针对不同配置源数据读取由对应的IConfigurationProvider进行读取,其实读取文件也是一样,针对于不同类型(物理文件、嵌入文件、云端文件等)文件,就由对应的IFileProvider的实现进行读取,下面详细说说;

正文

由于通过IFileProvider将目录文件进行抽象化,统一规范读取操作,使得读取不同地方的文件就显得更加方便,如物理文件、嵌入文件,只要有对应的实现即可;而框架针对物理文件和嵌入文件已经进行了具体实现,如下:

  • PhysicalFileProvider:物理文件提供程序,用来读取物理文件,就是平时使用的文件,不管是扩展名是什么;
  • EmbeddedFileProvider:嵌入文件提供程序,用来读取嵌入文件,就是程序编译时嵌入到程序集内部的文件,就像资源文件一样;
  • CompositeFileProvider:组合提供程序,同时可以读取物理文件和嵌入文件,就是可以指定多种数据源,这样的好处就是像操作同一个数据源一样;后续也可以与自定义的提供程序进行组合;

为了避免直接扒代码懵圈,先来个控制台例子,体验一下以上xxxProvider的使用:

运行结果:

读取物理文件是不是很简单,其实就是创建了一个PhysicalFileProvider对象时指定了一个路径,然后就能很方便的获取到对应目录下的信息;

嵌入文件也是如此,只需指定对应程序集即可(因为嵌入文件已经编译到程序集中),如下优化代码:

运行结果如下:

同样也是使用很简单,只是在创建EmbeddedFileProvider对象时指定一下对应的程序集即可,后续便可以用统一的方式进行文件和目录操作;

组合提供程序的目的就是将不同提供程序整合,就像使用同一个源一样,如下:

当然,按老套路走,不能用用就行了,继续扒扒代码,先看看IFileProvider:

  1. namespace Microsoft.Extensions.FileProviders
  2. {
  3. // IFileProvider定义的三个方法其实就是其对应的三大功能
  4. public interface IFileProvider
  5. {
  6. // 获取指定文件的信息,之后可以文件进行读取操作
  7. IFileInfo GetFileInfo(string subpath);
  8. // 获取指定目录下所有内容
  9. IDirectoryContents GetDirectoryContents(string subpath);
  10. // 用于监听文件改变
  11. IChangeToken Watch(string filter);
  12. }
  13. }

再来看看返回的IFileInfo和IDirectoryContents :

  1. namespace Microsoft.Extensions.FileProviders
  2. {
  3. public interface IFileInfo
  4. {
  5. // 标识是否存在
  6. bool Exists
  7. {
  8. get;
  9. }
  10. // 文件大小,如果不存在或是目录,这个值就是-1
  11. long Length
  12. {
  13. get;
  14. }
  15. // 对应的物理路径,其实就是文件的实际路径
  16. string PhysicalPath
  17. {
  18. get;
  19. }
  20. // 文件名字
  21. string Name
  22. {
  23. get;
  24. }
  25. // 文件最后的修改时间
  26. DateTimeOffset LastModified
  27. {
  28. get;
  29. }
  30. // 标识是否是目录
  31. bool IsDirectory
  32. {
  33. get;
  34. }
  35. // 返回的留可以进行文件读取
  36. Stream CreateReadStream();
  37. }
  38. // 其他信息继承了IFileInfo信息
  39. public interface IDirectoryContents : IEnumerable<IFileInfo>, IEnumerable
  40. {
  41. // 标识指定目录是否存在
  42. bool Exists
  43. {
  44. get;
  45. }
  46. }
  47. }

IChangeToken 之前在配置文件监听的时候有提到过,是用来监听到文件改变时进行发送通知的,这里就不深入了,感兴趣的小伙伴可以研究研究;

PhysicalFileProvider和EmbeddedFileProvider两个挑PhysicalFileProvider这个看看,后者小伙伴私下去扒吧:

  1. namespace Microsoft.Extensions.FileProviders
  2. {
  3. // 这里只挑了几个关键方法说明,其他属性和方法删除
  4. public class PhysicalFileProvider : IFileProvider, IDisposable
  5. {
  6. // 判断路径是否在指定的根路径下
  7. private bool IsUnderneathRoot(string fullPath)
  8. {
  9. return fullPath.StartsWith(Root, StringComparison.OrdinalIgnoreCase);
  10. }
  11. // 获取指定路径文件的FileInfo信息
  12. public IFileInfo GetFileInfo(string subpath)
  13. {
  14. // 判断路径是否处匹配
  15. if (string.IsNullOrEmpty(subpath) || PathUtils.HasInvalidPathChars(subpath))
  16. {
  17. return new NotFoundFileInfo(subpath);
  18. }
  19. // 判断指定的路径是否是在根目录下
  20. subpath = subpath.TrimStart(_pathSeparators);
  21. if (Path.IsPathRooted(subpath))
  22. {
  23. return new NotFoundFileInfo(subpath);
  24. }
  25. // 获取全路径,因为一般在外面操作是根据相对路径进行操作
  26. string fullPath = GetFullPath(subpath);
  27. if (fullPath == null)
  28. {
  29. return new NotFoundFileInfo(subpath);
  30. }
  31. // 构建了一个文件信息,包含文件的的操作和属性;
  32. FileInfo fileInfo = new FileInfo(fullPath);
  33. if (FileSystemInfoHelper.IsExcluded(fileInfo, _filters))
  34. {
  35. return new NotFoundFileInfo(subpath);
  36. }
  37. // 封装成PhysicalFileInfo对象
  38. return new PhysicalFileInfo(fileInfo);
  39. }
  40. // 获取指定目录下的所有内容
  41. public IDirectoryContents GetDirectoryContents(string subpath)
  42. {
  43. try
  44. {
  45. // 路径校验和上面一样
  46. if (subpath == null || PathUtils.HasInvalidPathChars(subpath))
  47. {
  48. return NotFoundDirectoryContents.Singleton;
  49. }
  50. subpath = subpath.TrimStart(_pathSeparators);
  51. if (Path.IsPathRooted(subpath))
  52. {
  53. return NotFoundDirectoryContents.Singleton;
  54. }
  55. string fullPath = GetFullPath(subpath);
  56. if (fullPath == null || !Directory.Exists(fullPath))
  57. {
  58. return NotFoundDirectoryContents.Singleton;
  59. }
  60. // 封装为PhysicalDirectoryContents对象
  61. return new PhysicalDirectoryContents(fullPath, _filters);
  62. }
  63. catch (DirectoryNotFoundException)
  64. {
  65. }
  66. catch (IOException)
  67. {
  68. }
  69. return NotFoundDirectoryContents.Singleton;
  70. }
  71. // 用监听文件改变的,通过文件匹配模式来指定需要监控的文件
  72. public IChangeToken Watch(string filter)
  73. {
  74. if (filter == null || PathUtils.HasInvalidFilterChars(filter))
  75. {
  76. return NullChangeToken.Singleton;
  77. }
  78. filter = filter.TrimStart(_pathSeparators);
  79. return FileWatcher.CreateFileChangeToken(filter);
  80. }
  81. }
  82. }

以上GetDirectoryContents和GetFileInfo分别返回的PhysicalDirectoryContents和PhysicalFileInfo才是关键,进去瞅瞅:

  1. public class PhysicalDirectoryContents : IDirectoryContents, IEnumerable<IFileInfo>, IEnumerable
  2. {
  3. // 用于存放指定目录下的全部内容的
  4. private IEnumerable<IFileInfo> _entries;
  5. // 判断指定目录是否存在
  6. public bool Exists => Directory.Exists(_directory);
  7. // 读取目录内容的关键方法
  8. private void EnsureInitialized()
  9. {
  10. try
  11. {
  12. // 根据指定的目录,获取目录下的所有内容,将其保存在集合中
  13. _entries = new DirectoryInfo(_directory).EnumerateFileSystemInfos().Where((Func<FileSystemInfo, bool>)((FileSystemInfo info) => !FileSystemInfoHelper.IsExcluded(info, _filters))).Select((Func<FileSystemInfo, IFileInfo>)delegate (FileSystemInfo info)
  14. {
  15. // 将取到的内容封装为PhysicalFileInfo对象
  16. FileInfo fileInfo = info as FileInfo;
  17. if (fileInfo != null)
  18. {
  19. return new PhysicalFileInfo(fileInfo);
  20. }
  21. // 将取到的内容封装为PhysicalFileInfo对象
  22. DirectoryInfo directoryInfo = info as DirectoryInfo;
  23. if (directoryInfo != null)
  24. {
  25. return new PhysicalDirectoryInfo(directoryInfo);
  26. }
  27. throw new InvalidOperationException("Unexpected type of FileSystemInfo");
  28. });
  29. }
  30. catch (Exception ex) when (ex is DirectoryNotFoundException || ex is IOException)
  31. {
  32. _entries = Enumerable.Empty<IFileInfo>();
  33. }
  34. }
  35. }

PhysicalFileInfo

  1. // 其实里面就是封装了IO文件操作的相关属性和操作
  2. public class PhysicalFileInfo : IFileInfo
  3. {
  4. // 文件信息,就是平时咱们直接读取到文件的那些信息
  5. private readonly FileInfo _info;
  6. // 是否存在
  7. public bool Exists => _info.Exists;
  8. // 文件大小
  9. public long Length => _info.Length;
  10. // 文件的全路径
  11. public string PhysicalPath => _info.FullName;
  12. // 文件名称
  13. public string Name => _info.Name;
  14. // 文件的最后修改时间
  15. public DateTimeOffset LastModified => _info.LastWriteTimeUtc;
  16. // 默认就是false,所以这里只能对文件有效
  17. public bool IsDirectory => false;
  18. public PhysicalFileInfo(FileInfo info)
  19. {
  20. _info = info;
  21. }
  22. // 获取文件流,并设置了只读权限
  23. public Stream CreateReadStream()
  24. {
  25. int bufferSize = 1;
  26. // 这里就熟悉了,平时直接读取文件就是这样的
  27. return new FileStream(PhysicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize, FileOptions.SequentialScan | FileOptions.Asynchronous);
  28. }
  29. }

好了,到这其实差不多就明白了,至少知道为什么IFileInfo只能获取到文件件信息,目录信息获取不到;至少在写文件的时候不再懵逼的在想:为什么不能写文件了,如果直接用返回的流进行文件写操作,就会报以下错:

总结

框架只是实现了本地读取的两个IFileProvider,如果针对于云端文件、FTP文件等有统一的读取需求,则就需要自己实现了;所以源码是不错的参考,封装之后,结合组合提供程序,后续使用就能像使用本地文件一样简便;

加上这篇,总共十五篇,把.NetCore中比较关键的核心都过了一遍,其中包含了启动流程、依赖注入、配置、选项、日志、中间件、文件,在每个章节中都会针对对应的核心类型进行源代码分析,虽然只是浅读,但也能明白其中缘由;后续的文章将会偏应用,比如静态文件目录配置、API的最佳实现、JWT使用、IdentityServer4的集成等等一堆组件的应用;

同时,后续将同步开启另一个专题:跟我一起学Redis,欢迎一起来学习;

------------------------------------------------

CSDN:Code综艺圈

知乎:Code综艺圈

掘金:Code综艺圈

博客园:Code综艺圈

bilibili:Code综艺圈

------------------------------------------------

一个被程序搞丑的帅小伙,关注"Code综艺圈",识别关注跟我一起学~~~

撸文不易,莫要白瞟,三连走起~~~~

跟我一起学.NetCore之文件系统应用及核心浅析的更多相关文章

  1. 跟我一起学.NetCore之日志(Log)模型核心

    前言 鲁迅都说:没有日志的系统不能上线(鲁迅说:这句我没说过,但是在理)!日志对于一个系统而言,特别重要,不管是用于事务审计,还是用于系统排错,还是用于安全追踪.....都扮演了很重要的角色:之前有很 ...

  2. 跟我一起学.NetCore之选项(Options)核心类型简介

    前言 .NetCore中提供的选项框架,我把其理解为配置组,主要是将服务中可供配置的项提取出来,封装成一个类型:从而服务可根据应用场景进行相关配置项的设置来满足需求,其中使用了依赖注入的形式,使得更加 ...

  3. 跟我一起学.NetCore之静态文件处理的那些事

    前言 如今前后端分离开发模式如火如荼,开发职责更加分明(当然前后端一起搞的模式也没有完全褪去):而对于每个公司产品实施来说,部署模式会稍有差别,有的会单独将前端文件部署为一个站点,有的会将前端文件和后 ...

  4. 跟我一起学.NetCore之WebApi接口裸奔有风险(Jwt)

    前言 撸码需谨慎,裸奔有风险.经常在一些技术交流群中了解到,还有很多小伙伴的项目中Api接口没有做任何安全机制验证,直接就裸奔了,对于一些临时项目或是个人小项目还好,其余的话,建议小伙伴们酌情考虑都加 ...

  5. 跟我一起学.NetCore之熟悉的接口权限验证不能少(Jwt)

    前言 权限管控对于一个系统来说是非常重要的,最熟悉不过的是菜单权限和数据权限,上一节通过Jwt实现了认证,接下来用它实现接口权限的验证,为什么不是菜单权限呢?对于前后端分离而言,称其为接口权限感觉比较 ...

  6. 跟我一起学.NetCore之MVC过滤器,这篇看完走路可以仰着头走

    前言 MVC过滤器在之前Asp.Net的时候就已经广泛使用啦,不管是面试还是工作,总有一个考点或是需求涉及到,可以毫不疑问的说,这个技术点是非常重要的: 在之前参与的面试中,得知很多小伙伴只知道有一两 ...

  7. 跟我一起学.NetCore之中间件(Middleware)简介和解析请求管道构建

    前言 中间件(Middleware)对于Asp.NetCore项目来说,不能说重要,而是不能缺少,因为Asp.NetCore的请求管道就是通过一系列的中间件组成的:在服务器接收到请求之后,请求会经过请 ...

  8. 跟我一起学.NetCore之中间件(Middleware)应用和自定义

    前言 Asp.NetCore中的请求管道是通过一系列的中间件组成的,使得请求会根据需求进行对应的过滤和加工处理.在平时开发中会时常引用别人定义好的中间件,只需简单进行app.Usexxx就能完成中间件 ...

  9. 跟我一起学.NetCore之路由的最佳实现

    前言 路由,这词绝对不陌生,不管在前端还是后端都经常提到,而这节不说其他,就聊.NetCore的路由:在之前的Asp.Net MVC 中,路由算是面试时必问的考点,可见其重要性,它的主要作用是映射UR ...

随机推荐

  1. 存储系列之 共享文件:链接link

    一.link与unlink的定义 1.link link是Linux文件系统目录管理的一个系统调用,创建一个链接,该链接只是创建一个目录项,上文ext2的介绍中提到过目录项是<文件名,inode ...

  2. linux手动安装python

    前提:你的linux服务器必须有gcc编译器,gcc查看方法:linux命令行>gcc -v 如果返回版本信息证明已经安装, 如果找不到命令,跳到这篇手动安装gcc >>> l ...

  3. 土地购买 (斜率优化dp)

    土地购买 (斜率优化dp) 题目描述 农夫 \(John\) 准备扩大他的农场,他正在考虑$ N(1 \leqslant N \leqslant 50,000)$ 块长方形的土地. 每块土地的长宽满足 ...

  4. PAT 2-13. 两个有序序列的中位数(25)

    题目链接:http://www.patest.cn/contests/ds/2-13 解题思路及代码如下: /* 解题思路: 分别求出序列A 和B 的中位数,设为a 和b,求序列A 和B 的中位数过程 ...

  5. 关于make及makefile的工作笔记

    之前一直是用java的,最近工作中需要在Linux中写一个C++程序,之前的写法很不规范,只有一个CPP.记录一下关于makefile的相关知识 想要完整的了解相关内容,推荐看这本书<程序员的自 ...

  6. Java泛型详解,通俗易懂只需5分钟

    转载出处:http://www.weixueyuan.net/view/6321.html 我们知道,使用变量之前要定义,定义一个变量时必须要指明它的数据类型,什么样的数据类型赋给什么样的值. 假如我 ...

  7. @SuppressWarnings注解用法详解(转)

    原文连接https://blog.csdn.net/sysware_carol/article/details/52100580 今天来谈谈@SuppressWarnings注解的作用. J2SE 提 ...

  8. idea git拉取、合并、处理冲突、提交代码具体操作

    早在两个月前我还在用eclipse开发,并且也发布的一些eclipse git的相关操作(操作都是本人亲自实践过的),但由于项目团队要求,开发工具统一用idea,实在不得已而为之切换了开发工具, 初次 ...

  9. 存储池与存储卷,使用virt-install创建虚拟机

    原文链接:https://www.cnblogs.com/zknublx/p/9199658.html 创建存储池 1.建立存储池的目录 mkdir /kvm/images 2.为了安全性,更改目录的 ...

  10. Reinforcement Learning Using a Continuous Time Actor-Critic Framework with Spiking Neurons

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! Abstract 动物会重复奖励的行为,但基于奖励的学习的生理基础仅得到了部分阐明.一方面,实验证据表明神经调节剂多巴胺携带有关奖励的信息 ...