跟我一起学.NetCore之文件系统应用及核心浅析
前言
在開發過程中,肯定避免不了讀取文件操作,比如讀取配置文件、上傳和下載文件、Web中html、js、css、圖片等靜態資源的訪問;在配置文件讀取章節中有說到,針對不同配置源數據讀取由對應的IConfigurationProvider進行讀取,其實讀取文件也是一樣,針對于不同類型(物理文件、嵌入文件、云端文件等)文件,就由對應的IFileProvider的實現進行讀取,下面詳細說說;
正文
由于通過IFileProvider將目錄文件進行抽象化,統一規范讀取操作,使得讀取不同地方的文件就顯得更加方便,如物理文件、嵌入文件,只要有對應的實現即可;而框架針對物理文件和嵌入文件已經進行了具體實現,如下:
PhysicalFileProvider:物理文件提供程序,用來讀取物理文件,就是平時使用的文件,不管是擴展名是什么;
EmbeddedFileProvider:嵌入文件提供程序,用來讀取嵌入文件,就是程序編譯時嵌入到程序集內部的文件,就像資源文件一樣;
CompositeFileProvider:組合提供程序,同時可以讀取物理文件和嵌入文件,就是可以指定多種數據源,這樣的好處就是像操作同一個數據源一樣;后續也可以與自定義的提供程序進行組合;
為了避免直接扒代碼懵圈,先來個控制臺例子,體驗一下以上xxxProvider的使用:
運行結果:
讀取物理文件是不是很簡單,其實就是創建了一個PhysicalFileProvider對象時指定了一個路徑,然后就能很方便的獲取到對應目錄下的信息;
嵌入文件也是如此,只需指定對應程序集即可(因為嵌入文件已經編譯到程序集中),如下優化代碼:
運行結果如下:
同樣也是使用很簡單,只是在創建EmbeddedFileProvider對象時指定一下對應的程序集即可,后續便可以用統一的方式進行文件和目錄操作;
組合提供程序的目的就是將不同提供程序整合,就像使用同一個源一樣,如下:
當然,按老套路走,不能用用就行了,繼續扒扒代碼,先看看IFileProvider:
再來看看返回的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之文件系统应用及核心浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 初识ABP vNext(11):聚合根、
- 下一篇: java信息管理系统总结_java实现科