當前位置:
首頁 > 最新 > 一個抽象的「文件系統」

一個抽象的「文件系統」

ASP.NET Core 具有很多針對文件讀取的應用。比如我們傾向於採用JSON文件來定義配置,所以應用就會涉及針對配置文件讀取。如果用戶發送一個針對物理文件的HTTP請求,應用會根據指定的路徑讀取目標文件的內容並對請求予以響應。在一個ASP.NET Core MVC應用中,針對View的動態編譯會涉及到根據預定義的路徑映射關係來讀取目標View文件。這些不同應用場景都會出現一個IFileProvider對象的身影,以此對象為核心的文件系統提供了統一的API來讀取文件的內容並監控內容的改變。

01

一個抽象的「文件系統」

所謂的「文件系統」有點名不副實,其實根本算不上一個系統,它僅僅是利用一個抽象化IFileProvider介面以統一的方式提供所需的文件而已。不過筆者實在想不到一個更為貼切的描述短語,所以還是姑且稱之為文件系統吧。因為IFileProvider自身是個抽象的介面,所以由它構建的也是一個抽象的文件系統。

這個文件系統採用目錄的方式來組織和規劃文件,但是這裡所謂的目錄和文件都是一個抽象的概念,並非對一個具體物理目錄和文件的映射。文件系統的目錄僅僅是文件的邏輯容器,而文件可能對應一個物理文件,也可能保存在資料庫中,或者來源於網路,甚至有可能根本就不存在,其內容需要在讀取時動態生成。為了讓讀者朋友們能夠對這個文件系統具有一個大體認識,我們先來演示幾個簡單的實例。

02

呈現文件系統的結構

文件系統中的文件以目錄的形式進行組織,一個IFileProvider可以視為針對一個根目錄的映射。目錄除了可以存放文件之外,還可以包含多個子目錄,所以目錄/文件在整體上呈現出樹形層次化結構。接下來我們利用提供的IFileProvider對象並將它映射到一個物理目錄,並利用它將所在目錄的整個結構呈現出來。

我們創建一個控制台應用,並添加如下兩個NuGet包。由於IFileProvider介面定義在「Microsoft.Extensions.FileProviders.Abstractions」這個NuGet包中,針對物理文件的IFileProvider實現類型PhysicalFileProvider所在的NuGet包名為「Microsoft.Extensions.FileProviders.Physical」,所以我們只需要添加後者的依賴即可。猶豫我們會利用.NET Core的依賴注入框架來提供服務對象,所以我們還添加了針對「Microsoft.Extensions.DependencyInjection」這個NuGet包的依賴。

Microsoft.Extensions.FileProviders.Physical

Microsoft.Extensions.DependencyInjection

我們定義了如下一個IFileManager介面,它利用一個唯一的方法ShowStructure將文件系統的整體結構顯示出來。該方法具有一個類型為Action的參數負責將文件系統的節點(目錄或者文件)呈現出來。對於這個Action委託對象的兩個泛型參數,第一個整型參數代表縮進的層級,後一個代表需要顯示的目錄或者文件的名稱。

如下所示的是實現了上面這個IFileManager介面的FileManager類。構建文件系統的IFileProvider對象對應著同名的只讀屬性,該屬性在構造函數中通過對應的參數進行賦值。目標文件系統的整體結構最終是通過Render方法以遞歸的方式呈現出來的,這其中涉及到IFileProvider的GetDirectoryContents方法的調用。該方法返回一個IDirectoryContents對象表示由指定路徑指向的目錄內容,如果對應的目錄存在,我們可以遍歷該對象得到它的子目錄和文件。目錄和文件通過一個IFileInfo對象來表示,至於究竟是目錄還是文件,則通過其屬性IsDirectory來區分。

接下來我們為演示的IFileProvider構建一個映射的物理目錄。我們將「C:Test」目錄作為根目錄,並按照如圖1所示的結構在它下面創建相應的子目錄和文件。我們將利用映射為該目錄的IFileProvider創建上面定義的這個FileManager,那麼調用它的ShowStructure方法應該呈現出與物理目錄完全一致的結構。

圖1 FileProvider映射的物理目錄結構

我們在Main方法中編寫了如下的演示程序。我們針對目錄「C:Test」創建了一個PhysicalFileProvider對象,並採用服務介面類型IFileProvider註冊到ServiceCollection對象上。除此之外,註冊到同一個ServiceCollection對象上的還有IFileManager和FileManager之間的映射。

我們最終利用ServiceCollection生成的IServiceProvider得到FileManager對象,並調用其ShowStructure方法將PhysicalFileProvider對象映射的目錄結構呈現出來。當我們運行該程序之後,控制台上將呈現出如圖2所示的輸出結果,該結果為我們展示了映射物理目錄的真實結構。

圖2 運行程序顯示的目錄結構。

03

讀取文件內容

上面我們演示了如何利用IFileProvider將文件系統的結構完整地呈現出來,接下來我們來演示如何利用它來讀取一個具體文件的內容。我們為IFileManager定義如下一個ReadAllTextAsync方法以非同步的方式讀取指定路徑對應的文件,並以字元串的形式返回讀取的內容。FileManager依然利用一個IFileProvider來完成針對文件的讀取工作。具體來說,它將指定的文件路徑作為參數調用其GetFileInfo方法並得到一個IFileInfo對象。接下來,我們調用IFileInfo的CreateReadStream得到讀取文件的輸出流,並利用後者得到文件的真實內容,最終採用最簡單的ASCII碼轉換成返回的字元串。

假設我們依然將FileManager使用的IFileProvider映射為目錄「C:Test」,現在我們該目錄中創建一個名為data.txt的文本文件,並在該文件中任意寫入一些內容。接下來我們在Main方法中編寫了如下的程序利用依賴注入的方式得到FileManager對象,並讀取文件data.txt的內容。最終的調試斷言旨在確定通過IFileProvider讀取的確實就是目標文件的真實內容。

我們一直在強調由IFileProvider構建的是一個抽象的具有目錄結構的文件系統,具體文件的提供方式取決於具體對IFileProvider介面的實現。由於我們定義的FileManager並沒有限定具體使用何種類型的IFileProvider,後者是在應用中通過依賴注入的方式指定的。

由於上面的應用程序注入的是一個PhysicalFileProvider對象,所以我們可以利用它來讀取對應目錄下的某個文件。假設現在我們將這個data.txt直接以資源文件的形式編譯到程序集中,我們就需要使用另一個名為EmbeddedFileProvider的類型,它定義在NuGet包「Microsoft.Extensions.FileProviders.Embedded」中。

現在我們直接將這個data.txt文件添加到控制台應用的項目根目錄下。在默認的情況下,當我們編譯項目的時候這樣的文件並不能成為內嵌到目標程序集的資源文件,為此我們修改項目文件.csproj的內容。具體來說,我們可以在.csproj文件中添加如下一個節點,並利用其子節點將文件data.txt設置為內嵌到編譯後生成的程序集的內嵌資源文件。

在添加了針對NuGet包「Microsoft.Extensions.FileProviders.Embedded」的依賴諸侯,我們編寫了如下的程序來演示針對內嵌於程序集中的資源文件的讀取。我們首先得到當前入口程序集,並利用它創建了一個EmbeddedFileProvider,後者替換原來的PhysicalFileProvider對象被註冊到ServiceCollection之中。我們接下來採用與上面完全一致的編程方式得到FileManager對象並利用它讀取內嵌文件data.txt的內容。為了驗證讀取的目標文件準確無誤,我們採用直接讀取資源文件的方式得到了內嵌文件data.txt的內容,並利用一個調試斷言確定兩者的一致性。

04

監控文件的變化

在文件讀取場景中,應用數據與源文件的同步是一個很常見的需求。比如說我們將配置定義在一個JSON文件中,應用啟動的時候會讀取該文件並根據配置數據對應用作相應的設置。在很多情況下,如果我們改動了配置文件, 最新的配置數據只有在應用重啟之後才能生效。如果我們能夠以一種高效的方式對配置文件進行監控,並在其發生改變的情況下嚮應用發送通知,那麼應用就能在不用重啟的情況下重新讀取配置文件,進而實現應用配置和原始配置文件的同步。

對文件系統實施監控並在發生改變時發送通知也是IFileProvider對象的核心功能之一。接下來我們依然使用上面這個控制台文件來演示如何使用PhysicalFileProvider來對某個物理文件試試監控,並在目標文件的內容發生改變的時候重新讀取新的內容。定義在Main方法上的整個程序代碼如下所示。

如上面的代碼片段所示,我們針對目錄「c: est」創建了一個PhysicalFileProvider對象,並調用其Watch方法對指定的文件data.txt實施監控。該方法的返回類型為IChangeToken,我們正是利用這個對象接收文件改變的通知。我們調用ChangeToken的靜態方法OnChange針對這個對象註冊了一個回調,當源文件發生改變的時候,註冊的回調會自動執行,進而實現對源文件的重新讀取和顯示。在程序的末端,我們以每隔5秒的間隔對文件data.txt作一次修改,而文件的內容為當前時間。所以當我們的程序啟動之後,每隔5秒鐘當前時間就會以如圖3的方式呈現在控制台上。

圖3 實時顯示監控文件的內容


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 大內老A 的精彩文章:

NET Core跨平台的奧秘2:歷史的枷鎖下

TAG:大內老A |