.NET Core的文件系統[3]:由PhysicalFileProvider構建的物理文件系統

来源:http://www.cnblogs.com/artech/archive/2016/07/31/net-core-file-provider-03.html
-Advertisement-
Play Games

ASP.NET Core應用中使用得最多的還是具體的物理文件,比如配置文件、View文件以及網頁上的靜態文件,物理文件系統的抽象通過PhysicalFileProvider這個FileProvider來實現,該類型定義在NuGet包“Microsoft.Extensions.FileProvider... ...


ASP.NET Core應用中使用得最多的還是具體的物理文件,比如配置文件、View文件以及網頁上的靜態文件,物理文件系統的抽象通過PhysicalFileProvider這個FileProvider來實現,該類型定義在NuGet包“Microsoft.Extensions.FileProviders.Physical”中。我們知道System.IO命名空間下定義了一整套針操作物理目錄和文件的API,實際上PhysicalFileProvider最終也是通過調用這些API來完成相關的IO操作的。[ 本文已經同步到《ASP.NET Core框架揭秘》之中]

目錄
一、PhysicalFileProvider
二、PhysicalFileInfo
三、PhysicalDirectoryInfo
四、針對物理文件的監控
五、總結

一、PhysicalFileProvider

如下所示的代碼片段展示PhysicalFileProvider類型的定義。

   1: public class PhysicalFileProvider : IFileProvider, IDisposable
   2: {   
   3:     public PhysicalFileProvider(string root);   
   4:      
   5:     public IFileInfo GetFileInfo(string subpath);  
   6:     public IDirectoryContents GetDirectoryContents(string subpath); 
   7:     public IChangeToken Watch(string filter);
   8:  
   9:     public void Dispose();   
  10: }


二、PhysicalFileInfo

一個PhysicalFileProvider對象總是映射到某個具體的物理目錄下,被映射的目錄所在的路徑通過構造函數的參數root來提供,該目錄將作為PhysicalFileProvider的根目錄。GetFileInfo方法返回的FileInfo對象代表指定路徑對應的文件,這是一個類型為PhysicalFileInfo的對象,如下所示的代碼片段展示了該類型的完整定義。一個物理文件可以通過一個System.IO.FileInfo對象來表示,一個PhysicalFileInfo對象實際上就是對這個一個FileInfo對象的封裝,定義在PhysicalFileInfo的所有屬性都來源於這個FileInfo對象。對於創建讀取文件輸出流的CreateReadStream方法來說,它返回的是一個根據物理文件絕對路徑創建的FileStream對象。

   1: public class PhysicalFileInfo : IFileInfo
   2: {
   3:     ...
   4:     public PhysicalFileInfo(FileInfo info);    
   5: }

對於PhysicalFileProvider的GetFile方法來說,即使我們指定的路徑指向一個具體的物理文件,它並不總是會返回一個PhysicalFileInfo對象。具體來說,PhysicalFileProvider會將如下幾種場景視為“目標文件不存在”,並讓GetFile返回一個NotFoundFileInfo對象。顧名思義,NotFoundFileInfo表示的正式一個“不存在”的文件,即它的Exists屬性總是返回False,而其他的屬性則變得沒有任何意義。當我們調用它的CreateReadStream試圖讀取一個根本不存在的文件內容時,會拋出一個FileNotFoundException類型的異常。

  • 確實沒有一個物理文件與指定的路徑相匹配。
  • 如果指定的是一個絕對路徑(比如“c:\foobar”),即Path.IsPathRooted返回返回True。
  • 如果指定的路徑指向一個隱藏文件。

三、PhysicalDirectoryInfo

對於PhysicalFileProvider來說,它利用PhysicalFileInfo對象來描述某個具體的物理文件,針對目錄的描述則通過一個類型為PhysicalDirectoryInfo的對象。既然PhysicalFileInfo是對一個System.IO.FileInfo對象的封裝,那麼我們應該想得到PhysicalDirectoryInfo封裝的自然就是表示目錄的DirectoryInfo對象。如下麵的代碼片段所示,我們需要在創建一個PhysicalDirectoryInfo對象時提供這個DirectoryInfo對象,PhysicalDirectoryInfo實現的所有屬性的返回值都來源於這個DirectoryInfo對象。由於CreateReadStream方法的目的是讀取文件的內容,所以當我們調用一個PhysicalDirectoryInfo對象的這個方法的時候,會拋出一個InvalidOperationException類型的異常。

   1: public class PhysicalDirectoryInfo : IFileInfo
   2: {   
   3:     ...
   4:     public PhysicalDirectoryInfo(DirectoryInfo info);
   5: }

當我們調用PhysicalFileProvider的GetDirectoryContents方法時,如果指定的路徑指向一個具體的目錄,那麼該方法會返回一個類型為EnumerableDirectoryContents的對象,不過EnumerableDirectoryContents僅僅是一個在編程過程中不可見的內部類型。EnumerableDirectoryContents是一個FileInfo對象的集合,該集合中會包括所有描述子目錄的PhysicalDirectoryInfo對象和描述文件的PhysicalFileInfo對象。至於EnumerableDirectoryContents的Exists屬性,它總是返回True。如果指定的路徑並不指向一個存在目錄,或者指定的是一個絕對路徑,這個方法都會返回一個Exsits屬性總是返回False的NotFoundDirectoryContents對象。

四、針對物理文件的監控

我們接著來談談PhysicalFileProvider的Watch方法。當我們調用該方法的時候,PhysicalFileProvider會通過解析我們提供的篩選表達式確定我們期望監控的文件,然後利用FileSystemWatcher對象來對這些文件試試監控。針對這些文件的變化(創建、修改、重命名和刪除)都會實時地反映到Watch方法返回的ChangeToken上。 值得一提的是,FileSystemWatcher類型實現IDisposable介面,PhysicalFileProvider也實現了相同的介面,PhysicalFileProvider的Dispose方法的唯一使命就是釋放這個FileSystemWatcher對象

Watch方法中指定的篩選表達式必須是針對當前PhysicalFileProvider根目錄的相對路徑,可以使用“/”或者“./”首碼,也可以不採用任何首碼。一旦我們使用了絕對路徑(比如“c:\test\*.txt”)或者“../”首碼(比如“../test/*.txt”),不論解析出來的文件是否存在於PhysicalFileProvider的根目錄下,這些文件都不會被監控。除此之外,如果我們沒有指定任何篩選條件,也不會有任何的文件會被監控。

監控文件變化的真正目的在於讓應用程式能夠及時感知到數據源的改變,進而自動執行某些預先註冊的回掉操作。回調的註冊可以直接通過調用ChangeToken的RegisterChangeCallback方法來完成,註冊的回調通過一個類型為Action<object>的委托對象來表示。對於在第一節演示的文件監控的實例,相應的程式“照理說”可以改寫成如下的形式。

   1: IFileProvider fileProvider = new PhysicalFileProvider(@"c:\test");
   2: fileProvider.Watch("data.txt").RegisterChangeCallback(_ = >LoadFileAsync(fileProvider), null);
   3: while (true)
   4: {
   5:     File.WriteAllText(@"c:\test\data.txt", DateTime.Now.ToString());
   6:     Task.Delay(5000).Wait();
   7: }
   8:  
   9: public static async void LoadFileAsync(IFileProvider fileProvider)
  10: {
  11:     Stream stream = fileProvider.GetFileInfo("data.txt").CreateReadStream();
  12:     {
  13:         byte[] buffer = new byte[stream.Length];
  14:         await stream.ReadAsync(buffer, 0, buffer.Length);
  15:         Console.WriteLine(Encoding.ASCII.GetString(buffer));
  16:     }
  17: }

如果執行上面這段程式,我們會發現只有第一個針對文件的更新能夠被感知,後續的文件更新操作將自動被忽略。導致這個問題的根源在於,單個ChangeToken對象的使命在於當綁定的數據源第一次發生變換時對外發送相應的信號,而不具有持續發送數據變換的能力。其實這一點從IChangeToken介面的定義就可以看出來,我們知道它具有一個HasChanged屬性表示數據是否已經發生變化,而並沒有提供一個讓這個屬性“複位”的方法。所以當我們需要對某個文件進行持續監控的時候,我們需要在註冊的回調中重新調用FileProvider的Watch方法,並利用生成ChangeToken再次註冊回調。除此之外,考慮到ChangeToken的RegisterChangeCallback方法以一個IDisposable對象的形式返回回調註冊對象,我們應該在對回調實施二次註冊時調用第一次返回的回調註冊對象的Dispose方法將其釋放掉。如下所示的程式才能達到對文件試試持續監控的目的。

   1: IFileProvider fileProvider = new PhysicalFileProvider(@"c:\test");
   2: Action<object> callback = null;
   3: IDisposable regiser = null;
   4: callback = _ =>
   5: {
   6:     regiser.Dispose();
   7:     LoadFileAsync(fileProvider);
   8:     fileProvider.Watch("data.txt").RegisterChangeCallback(callback, null);
   9: };
  10:  
  11: regiser = fileProvider.Watch("data.txt").RegisterChangeCallback(callback, null);

不過這樣的編程方式不但看起來比較繁瑣,很多對ChangeToken缺乏認識的人甚至對這樣的編程方式無法理解。為瞭解決這個問題,我們可以使用定義在ChangeToken類型中如下兩個方法OnChange方法來註冊數據發生改變時自動執行的回調。這兩個方法具有兩個參數,前者是一個用於創建ChangeToken對象的Func<IChangeToken>對象,後者則是代表回調操作的Action<object>/Action<TState>對象。實際上第一節的實例演示中我們就是調用的這個OnChange方法。

   1: public static class ChangeToken
   2: {
   3:     public static IDisposable OnChange(Func<IChangeToken> changeTokenProducer, Action changeTokenConsumer)
   4:     {        
   5:         Action<object> callback = null;
   6:         callback = delegate (object s) {
   7:             changeTokenConsumer();
   8:             changeTokenProducer().RegisterChangeCallback(callback, null);
   9:         };
  10:         return changeTokenProducer().RegisterChangeCallback(callback, null);
  11:     }
  12:  
  13:     public static IDisposable OnChange<TState>(Func<IChangeToken> changeTokenProducer, Action<TState> changeTokenConsumer, TState state)
  14:     {
  15:         Action<object> callback = null;
  16:         callback = delegate (object s) {
  17:             changeTokenConsumer((TState) s);
  18:             changeTokenProducer().RegisterChangeCallback(callback, s);
  19:         };
  20:         return changeTokenProducer().RegisterChangeCallback(callback, state);
  21:     }
  22: }

如果改用這個OnChange方法來替換掉原來手工調用ChangeToken的RegisterChangeCallback方法進行回調註冊的方式,原本顯得相對繁瑣的程式可以通過如下兩句代碼來替換。實際上在《讀取並監控文件的變化》中,我們調用的正是這個OnChange方法。

   1: IFileProvider fileProvider = new PhysicalFileProvider(@"c:\test");
   2: ChangeToken.OnChange(() => fileProvider.Watch("data.txt"), () => LoadFileAsync(fileProvider));


五、總結

我們藉助下圖所示的UML來對由PhysicalFileProvider構建物理文件系統的整體設計做一個簡單的總結。首先,該文件系統下用於描述目錄和文件的分別是一個PhysicalDirectoryInfo和PhysicalFileInfo對象,它們分別是對一個DirectoryInfo和FileInfo(System.IO.FileInfo)對象的封裝。PhysicalFileProvider的GetDirectoryContents方法返回一個EnumerableDirectoryContents對象(如果指定的目錄存在),組成該對象的分別是根據其所有子目錄和文件創建的PhysicalDirectoryInfo和PhysicalFileInfo對象。當我們調用PhysicalFileProvider的GetFileInfo方法時,如果指定的文件存在,返回的是描述該文件的PhysicalFileInfo對象。至於PhysicalFileProvider的Watch方法,它最終利用了FileSystemWatcher來監控指定文件的變化。

3


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 2016年到了一家公司做網管,經常會為了裝系統而煩惱,後來學習了WDS自動部署,但是在學習過程中發現啟動鏡像boot.wim中沒有網卡驅動 導致wds報錯,後來經過網上查找相關資料學會瞭如何向系統里添加驅動解決了我在啟動過程中找不到網卡驅動的問題。因為每次碰到不同的電腦有不同的網卡都 需要添加一次, ...
  • 在檢查一Linux伺服器時,發現日誌裡面有大量“lvm[12446]: Another thread is handling an event. Waiting...” Jul 4 00:01:42 localhost lvm[12446]: Another thread is handling a... ...
  • 首先啟動你的卡裡系統,等出現引導界面時選擇恢復模式。如下圖: 再出來一個界面時,選擇第二個並按E鍵進入編輯模式。如下圖: 進入編輯模式後找到如下圖的代碼: 把ro改為rw,並且在.gz 後面寫上init=/bin/bash,如下圖: 改完後按F10或Ctrl+X繼續啟動。啟動後的界面如下圖: 這時輸 ...
  • 為了定位問題,需要在Linux上使用tcpdump並且保存到文件,遇到瞭如下問題:Permission denied. ...
  • Unix下共有五種I/O模型: 1. 阻塞式I/O 2. 非阻塞式I/O 3. I/O復用(select和poll) 4. 信號驅動式I/O(SIGIO) 5. 非同步I/O(POSIX的aio_系列函數) io請求分兩步: 1. 先將數據從存儲介質(磁碟,網路等)拷貝到內核緩衝區,此時稱為數據準備好 ...
  • Linux伺服器日誌(Oracle Linux Server release 5.7)裡面出現了一些"Buffer I/O error on device dm-4, logical block 0"之類的錯誤,如下所示: Jul 3 02:33:24 localhost kernel: Buffe... ...
  • 關鍵詞: .bind() .unbind() .css() .hasclass() .removeclass .parent() .children() .html() .hide() .show() .attr() .val() .unbind() .bind()的使用 .css的使用 .pare ...
  • 解決辦法放在最前面,方便急需答案的同學: 創建了ftp使用的windows賬戶後,一定要給該賬戶添加ftp目錄的許可權,如下圖所示,為新賬戶添加許可權後(且設置了“ftp身份驗證”),即可正常訪問ftp: 說完解決方案,再聽我從頭道來: 要在一臺windows server 2008上部署一個支持匿名訪 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...