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

前言

在开发过程中,肯定避免不了读取文件操作,比如读取配置文件、上传和下载文件、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综艺圈",识别关注跟我一起学~~~

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307576.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习入门笔记(1)——导论部分

此笔记来源于 Sebastian Raschka 的 Introduction to Deep Learning 系列课程。 首先介绍的是传统的编程范式&#xff0c;假设我们想实现垃圾邮件识别的功能&#xff0c;传统的方法就是由程序员来找出垃圾邮件的规则并对其进行编程&#xff0c;得到一个垃圾邮件识别的程序。 机…

深度学习入门笔记(2)—— 感知器

最经典的神经元模型&#xff0c;从左到右依次是&#xff1a;输入、权重、加权和、阈值、输出。加权和又叫做 Net Input&#xff0c;符号为 z&#xff0c;当 z 的值大于阈值时输出 1&#xff0c;小于阈值时输出 0。 实现与门和或门&#xff0c;权重为 1&#xff0c;阈值分别为 1…

创建一个对象时,在一个类当中 静态代码块 和普通代码块构造方法 的顺序?

一:前言须知 普通代码块&#xff0c;在创建对象实例的时候&#xff0c;会被调用&#xff0c;每创建一次&#xff0c;就调用一次静态代码块&#xff0c;在类加载的时候执行&#xff0c;并且只会执行一次类加载的时机: 创建对象实例的时候&#xff08;new&#xff09;创建子类实…

ASP.NET Core 基于声明的访问控制到底是什么鬼?

从ASP.NET 4.x到ASP.NET Core&#xff0c;内置身份验证已从基于角色的访问控制(RBAC)转变为基于声明的访问控制(CBAC)。我们常用的HttpContext.User属性ASP.NET 4.0时代是IPrincipal类型&#xff0c;ASP.NETCore现在强化为ClaimsPrincipal类型。本文就一起来看看这难缠的、晦涩…

回溯的问题合集(Leetcode题解-Python语言)

78. 子集 class Solution:def subsets(self, nums: List[int]) -> List[List[int]]:ans []cur []def dfs(i):if i len(nums):ans.append(cur.copy())return# 包括 nums[i]cur.append(nums[i])dfs(i1)# 不包括 nums[i]cur.pop()dfs(i1)dfs(0)return ans要找出所有子集&a…

一个对象的创建流程

一:流程 加载Person类的信息,(也就是加载Person.class文件 只加载一次) 这个就是类加载的几个过程加载 ,将.class文件转化成二进制流加载到JVM的内存的方法区中&#xff0c;并在堆中生成一个Class对象验证准备解析初始化 该实例堆当中开辟空间 每个类的实例都会记得自己是由哪…

RabbitMq如何确保消息不丢失

上篇写了掌握Rabbitmq几个重要概念&#xff0c;从一条消息说起&#xff0c;这篇来总结关于消息丢失让人头痛的事情。网络故障、服务器重启、硬盘损坏等都会导致消息的丢失。消息从生产到消费主要结果以下几个阶段如下图。①生产阶段&#xff0c;生产者创建消息&#xff0c;经过…

LEETCODE PATTERNS Neetcode 刷题记录(Leetcode题解-Python语言)

LEETCODE PATTERNS 官网在这个链接&#xff0c;Neetcode 官网在这个链接 If input array is sorted then 遇到有序数组用二分或双指针 Binary searchTwo pointers If asked for all permutations/subsets then 求排列或子集用回溯 Backtracking If given a tree then 遇到树就用…

蓝桥杯-单词分析

一:题目 题目描述 小蓝正在学习一门神奇的语言&#xff0c;这门语言中的单词都是由小写英文字母组 成&#xff0c;有些单词很长&#xff0c;远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词&#xff0c;他准备不再完全记忆这些单词&#xff0c;而是根据单词中哪…

.NET Core 使用 Consul 服务注册发现

Consul是一个用来实现分布式系统服务发现与配置的开源工具。它内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案&#xff0c;不再需要依赖其他工具&#xff0c;使用起来也较为简单。Consul官网&#xff1a;https://www.consul.io开源地…

蓝桥杯-成绩统计

一:题目 题目描述 小蓝给学生们组织了一场考试&#xff0c;卷面总分为 100 分&#xff0c;每个学生的得分都是一个 0 到 100 的整数。 如果得分至少是 60 分&#xff0c;则称为及格。如果得分至少为 85 分&#xff0c;则称为优秀。 请计算及格率和优秀率&#xff0c;用百分数…

Mosh 的 MySQL 课程编程练习题目与答案

这篇文章是我观看 Mosh 的 MySQL 完整版课程进行题目练习的记录&#xff0c;视频的话去 B 站搜索就能找到&#xff0c;数据库文件的话可以从这里下载。 目录第二章2- SELECT 子句3- WHERE 子句4- 逻辑运算符5- IN 运算符6- BETWEEN 运算符7- LIKE 运算符8- REGEXP 运算符&#…

《ASP.NET Core 真机拆解》 送书活动结果公布

截至2020.09.26 本次送书活动 送福利 | 送书5本 ASP.NET Core 真机拆解 。下面把Top 5的留言截图给大家回顾一下。特别鸣谢作者罗志超提供的图书。以下5位同学将获赠书籍一本&#xff1a;傲慢的上校静阿花阿定傲慢与偏见以上同学请在2020年9月30日24&#xff1a;00之前加小二微…

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

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

深度学习入门笔记(3)——用梯度下降进行参数更新

首先是对感知器的简单回顾&#xff0c;假设现有的训练集为 D&#xff0c;共有 n 个训练数据&#xff0c;每个数据都有 m 个输入特征和一个输出标签。一个 epoch 就是遍历一次整个训练集&#xff0c;对于每一个训练数据&#xff0c;都计算其预测、计算误差、更新参数。 在一个 e…

ASP.NET Core Blazor Webassembly 之 路由

web最精妙的设计就是通过url把多个页面串联起来&#xff0c;并且可以互相跳转。我们开发系统的时候总是需要使用路由来实现页面间的跳转。传统的web开发主要是使用a标签或者是服务端redirect来跳转。那今天来看看Blazor是如何进行路由的。使用page指定组件的路由path我们可以在…

手撕单例模式(详解)

一:设计模式概述 1:设计模式的概念 软件设计模式&#xff08;Software Design Pattern&#xff09;&#xff0c;又称设计模式&#xff0c;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。它描述了在软件设计过程中的一些不断重复发生的问题&#xff0…

微软发布.NET 5.0 RC1,未来将只有一个.NET

本周早些时候&#xff0c;微软发布了.NET 5.0 RC1&#xff0c;这是.NET 5 在11 月正式发布之前的第一个“go-live”版本。.NET 5 是.NET 生态系统的统一平台&#xff0c;将所有组件打包成一个跨平台包。新版本包含了.NET Core 3 的很多改进&#xff0c;包括新的语言版本(C# 9 和…

深度学习入门笔记(4)—— Pytorch 和计算图的简单介绍

Pytorch 顾名思义&#xff0c;就是 Torch 7 移植到 Python 的版本&#xff0c;其最大亮点就是自动微分、动态计算图和 Numpy 集成。 Pytorch 相比于 Numpy&#xff0c;优点在于支持 GPU 计算甚至是多设备计算&#xff0c;以及动态计算图。 在 import 的时候&#xff0c;得记住 …

leetcode26. 删除有序数组中的重复项

一:题目 二:上码 // class Solution { // public: // int removeDuplicates(vector<int>& nums) { // //排序 // sort(nums.begin(),nums.end());// //调用unique&#xff08;&#xff09;将vector当中的相同元素 放到 容器的最后面 //…