前言

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

正文

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

  • PhysicalFileProvider:物理文件提供程序,用来读取物理文件,就是平时使用的文件,不管是扩展名是什么;

  • EmbeddedFileProvider:嵌入文件提供程序,用来读取嵌入文件,就是程序编译时嵌入到程序集内部的文件,就像资源文件一样;

  • CompositeFileProvider:组合提供程序,同时可以读取物理文件和嵌入文件,就是可以指定多种数据源,这样的好处就是像操作同一个数据源一样;后续也可以与自定义的提供程序进行组合;

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

运行结果:

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

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

运行结果如下:

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

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

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

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

再来看看返回的IFileInfo和IDirectoryContents :

namespace Microsoft.Extensions.FileProviders
{public interface IFileInfo{// 标识是否存在bool Exists{get;}// 文件大小,如果不存在或是目录,这个值就是-1long Length{get;}// 对应的物理路径,其实就是文件的实际路径string PhysicalPath{get;}// 文件名字string Name{get;}// 文件最后的修改时间DateTimeOffset LastModified{get;}// 标识是否是目录bool IsDirectory{get;}// 返回的留可以进行文件读取Stream CreateReadStream();}// 其他信息继承了IFileInfo信息public interface IDirectoryContents : IEnumerable<IFileInfo>, IEnumerable{// 标识指定目录是否存在bool Exists{get;}}
}

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

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

namespace Microsoft.Extensions.FileProviders
{// 这里只挑了几个关键方法说明,其他属性和方法删除public class PhysicalFileProvider : IFileProvider, IDisposable{// 判断路径是否在指定的根路径下private bool IsUnderneathRoot(string fullPath){return fullPath.StartsWith(Root, StringComparison.OrdinalIgnoreCase);}// 获取指定路径文件的FileInfo信息public IFileInfo GetFileInfo(string subpath){// 判断路径是否处匹配if (string.IsNullOrEmpty(subpath) || PathUtils.HasInvalidPathChars(subpath)){return new NotFoundFileInfo(subpath);}// 判断指定的路径是否是在根目录下subpath = subpath.TrimStart(_pathSeparators);if (Path.IsPathRooted(subpath)){return new NotFoundFileInfo(subpath);}// 获取全路径,因为一般在外面操作是根据相对路径进行操作string fullPath = GetFullPath(subpath);if (fullPath == null){return new NotFoundFileInfo(subpath);}// 构建了一个文件信息,包含文件的的操作和属性;FileInfo fileInfo = new FileInfo(fullPath);if (FileSystemInfoHelper.IsExcluded(fileInfo, _filters)){return new NotFoundFileInfo(subpath);}// 封装成PhysicalFileInfo对象return new PhysicalFileInfo(fileInfo);}// 获取指定目录下的所有内容public IDirectoryContents GetDirectoryContents(string subpath){try{  // 路径校验和上面一样if (subpath == null || PathUtils.HasInvalidPathChars(subpath)){return NotFoundDirectoryContents.Singleton;}subpath = subpath.TrimStart(_pathSeparators);if (Path.IsPathRooted(subpath)){return NotFoundDirectoryContents.Singleton;}string fullPath = GetFullPath(subpath);if (fullPath == null || !Directory.Exists(fullPath)){return NotFoundDirectoryContents.Singleton;}// 封装为PhysicalDirectoryContents对象return new PhysicalDirectoryContents(fullPath, _filters);}catch (DirectoryNotFoundException){}catch (IOException){}return NotFoundDirectoryContents.Singleton;}// 用监听文件改变的,通过文件匹配模式来指定需要监控的文件public IChangeToken Watch(string filter){if (filter == null || PathUtils.HasInvalidFilterChars(filter)){return NullChangeToken.Singleton;}filter = filter.TrimStart(_pathSeparators);return FileWatcher.CreateFileChangeToken(filter);}}
}

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

public class PhysicalDirectoryContents : IDirectoryContents, IEnumerable<IFileInfo>, IEnumerable
{// 用于存放指定目录下的全部内容的private IEnumerable<IFileInfo> _entries;// 判断指定目录是否存在public bool Exists => Directory.Exists(_directory);// 读取目录内容的关键方法private void EnsureInitialized(){try{// 根据指定的目录,获取目录下的所有内容,将其保存在集合中_entries = new DirectoryInfo(_directory).EnumerateFileSystemInfos().Where((Func<FileSystemInfo, bool>)((FileSystemInfo info) => !FileSystemInfoHelper.IsExcluded(info, _filters))).Select((Func<FileSystemInfo, IFileInfo>)delegate (FileSystemInfo info){// 将取到的内容封装为PhysicalFileInfo对象FileInfo fileInfo = info as FileInfo;if (fileInfo != null){return new PhysicalFileInfo(fileInfo);}// 将取到的内容封装为PhysicalFileInfo对象DirectoryInfo directoryInfo = info as DirectoryInfo;if (directoryInfo != null){return new PhysicalDirectoryInfo(directoryInfo);}throw new InvalidOperationException("Unexpected type of FileSystemInfo");});}catch (Exception ex) when (ex is DirectoryNotFoundException || ex is IOException){_entries = Enumerable.Empty<IFileInfo>();}}
}

PhysicalFileInfo

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

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

总结

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

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

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

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

CSDN:Code综艺圈

知乎:Code综艺圈

掘金:Code综艺圈

博客园:Code综艺圈

bilibili:Code综艺圈

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

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

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

跟我一起学.NetCore之文件系统应用及核心浅析相关推荐

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

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

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

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

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

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

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

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

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

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

  6. 跟我一起学.NetCore之Options实例演示及分析

    前言 来啦!来啦!上一节一堆代码,是不是感觉甚是无味啊?没关系,这里结合上一节内容专注举例演示,绝不废话!走起~~~~~ 正文 老规矩,一个WebApi项目走起,项目结构如下: 上一节中提到,Opti ...

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

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

  8. jwt 长度_跟我一起学.NetCore之WebApi接口裸奔有风险(Jwt)

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

  9. 重邮计算机学院新闻,感知重邮丨重庆邮电大学计算机学院稳固学科建设核心 提升人才培养质量...

    (原标题:感知重邮丨重庆邮电大学计算机学院稳固学科建设核心 提升人才培养质量) 华龙网7月12日14时30分讯(李婷婷 通讯员 敖永春)重庆邮电大学第二次党代会以来,计算机学院稳固学科建设核心,提升人 ...

最新文章

  1. LeetCode-笔记-48.旋转图像
  2. 图:BFS(深度优先搜索)图解分析代码实现
  3. docker中部署mysql
  4. 像素画高级教程:怎样画流动的水
  5. Android官方开发文档Training系列课程中文版:键盘输入处理之处理键盘按键
  6. paip.字符串操作uapi java php python总结..
  7. 我手撸了一个划线翻译工具!
  8. Python CGI 编程 | 类FieldStorage的使用
  9. 《淘宝网开店 拍摄 修图 设计 装修 实战150招》一一2.11 动感十足的S形构图
  10. karma如何与测试框架合作2之webpack
  11. 原码、反码、补码及位操作符,C语言位操作
  12. Tensorflow学习笔记-基于LeNet5结构的ORL数据集人脸识别
  13. 恢复数据的原理和方法
  14. 墨画子卿第三章第5节:飞过去的是胧月
  15. CSS 实现面包屑导航
  16. XML/JSON 语法随堂笔记
  17. js判断身份证是否合法
  18. SQLPub免费的MySQL数据库
  19. IoT大门上的鲁班锁:华为所铸的分布式安全
  20. SpringBoot集成TkMybatis

热门文章

  1. zbb20180710 maven Failed to read artifact descriptor--maven
  2. Unity经典游戏教程之:雪人兄弟
  3. Java基础 五 方法
  4. 【Tomcat】Tomcat配置与优化(内存、并发、管理)【自己配置】
  5. OC如何跳到系统设置里的各种设置界面
  6. Atitit. 木马病毒的外部class自动加载机制------加载class的方法总结
  7. [网摘]CSS z-index 属性
  8. 北京科技计算机与通信工程学院,北京科技大学计算机与通信工程学院-任超
  9. DexClassLoader的使用
  10. jQuery实现等比例缩放大图片让大图片自适应页面布局