天天看點

了解ASP.NET Core - 檔案伺服器(File Server)

注:本文隸屬于《了解ASP.NET Core》系列文章,請檢視置頂部落格或點選此處檢視全文目錄

靜态檔案預設存放在 Web根目錄(Web Root) 中,路徑為 項目根目錄(Content Root) 下的<code>wwwroot</code>檔案夾,也就是<code>{Content Root}/wwwroot</code>。

如果你調用了<code>Host.CreateDefaultBuilder</code>方法,那麼在該方法中,會通過<code>UseContentRoot</code>方法,将程式目前工作目錄(<code>Directory.GetCurrentDirectory()</code>)設定為項目根目錄。具體可以檢視主機一節。

當然,你也可以通過<code>UseWebRoot</code>擴充方法将預設的路徑<code>{Content Root}/wwwroot</code>修改為自定義目錄(不過,你改它幹啥捏?)

為了友善,後面均使用 wwwroot 來表示Web根目錄

首先,我們先在 wwwroot 檔案夾下建立一個名為 config.json 的檔案,内容随便填寫

注意,確定 wwwroot 下的檔案的屬性為“如果較新則複制”或“始終複制”。

接着,我們通過<code>UseStaticFiles</code>擴充方法,來注冊靜态檔案中間件<code>StaticFileMiddleware</code>:

現在,嘗試一下通過 http://localhost:5000/config.json 來擷取 wwwroot/config.json 的檔案内容吧

如果你的項目中啟用SwaggerUI,那麼你會發現,即使你沒有手動通過調用<code>UseStaticFiles()</code>添加中間件,你也可以通路 wwwroot 檔案下的檔案,這是因為 SwaggerUIMiddleware 中使用了 StaticFileMiddleware

上面我們已經能夠提供 wwwroot 檔案夾内的靜态檔案了,那如果我們的檔案不在 wwwroot 檔案夾内,那如何提供呢?

很簡單,我們可以針對<code>StaticFileMiddleware</code>中間件進行一些額外的配置,了解一下配置項:

假設我們現在有這樣一個檔案目錄結構:

wwwroot

config.json

files

file.json

然後,除了用于提供 wwwroot 靜态檔案的中間件外,我們還要注冊一個用于提供 files 靜态檔案的中間件:

建議将公開通路的檔案放置到 wwwroot 目錄下,而将需要授權通路的檔案放置到其他目錄下(在調用<code>UseAuthorization</code>之後調用<code>UseStaticFiles</code>并指定檔案目錄)

上面,我們可以通過Url通路某一個檔案的内容,而通過<code>UseDirectoryBrowser</code>,注冊<code>DirectoryBrowserMiddleware</code>中間件,可以讓我們在浏覽器中以目錄的形式來通路檔案清單。

另外,<code>DirectoryBrowserMiddleware</code>中間件的可配置項除了<code>SharedOptionsBase</code>中的之外,還有一個<code>Formatter</code>,用于自定義目錄視圖。

示例如下:

通過<code>UseDefaultFiles</code>,注冊<code>DefaultFilesMiddleware</code>中間件,允許在通路靜态檔案、但未提供檔案名的情況下(即傳入的是一個目錄的路徑),提供預設頁的展示。

注意:<code>UseDefaultFiles</code>必須在<code>UseStaticFiles</code>之前進行調用。因為<code>DefaultFilesMiddleware</code>僅僅負責重寫Url,實際上預設頁檔案,仍然是通過<code>StaticFilesMiddleware</code>來提供的。

預設情況下,該中間件會按照順序搜尋檔案目錄下的HTML頁面檔案:

default.htm

default.html

index.htm

index.html

另外,<code>DefaultFilesMiddleware</code>中間件的可配置項除了<code>SharedOptionsBase</code>中的之外,還有一個<code>DefaultFileNames</code>,是個清單,用于自定義預設頁的檔案名,裡面的預設值就是上面提到的4個檔案名。

<code>UseFileServer</code>內建了<code>UseStaticFiles</code>、<code>UseDefaultFiles</code>和<code>UseDirectoryBrowser</code>的功能,用起來友善一些,也是我們項目中使用的首選擴充方法。

先看一下<code>FileServerOptions</code>:

可以看到,<code>FileServerOptions</code>包含了<code>StaticFileOptions</code>、<code>DirectoryBrowserOptions</code>和<code>DefaultFilesOptions</code>三個選項,可以針對<code>StaticFileMiddleware</code>、<code>DirectoryBrowserMiddleware</code>和<code>DefaultFilesMiddleware</code>進行自定義配置。另外,其預設啟用了靜态檔案和預設頁,禁用了目錄浏覽。

下面舉個例子熟悉一下:

假設檔案目錄:

images

1.jpg

myindex.html

當通路 http://localhost:5000/files 時,由于在<code>DefaultFilesOptions.DefaultFileNames</code>中添加了檔案名<code>myindex.html</code>,是以可以找到預設頁,此時會顯示預設頁的内容。

假如我們沒有在<code>DefaultFilesOptions.DefaultFileNames</code>中添加檔案名<code>myindex.html</code>,那麼便找不到預設頁,但由于啟用了<code>DirectoryBrowsing</code>,是以此時會展示檔案清單。

上面我們已經見過<code>PhysicalFileProvider</code>了,它僅僅是衆多檔案提供程式中的一種。所有的檔案提供程式均實作了<code>IFileProvider</code>接口:

常用的檔案提供程式有以下三種:

PhysicalFileProvider

ManifestEmbeddedFileProvider

CompositeFileProvider

在介紹這三種檔案提供程式之前,先說一下<code>glob模式</code>,即<code>通配符模式</code>。兩個通配符分别是<code>*</code>和<code>**</code>。

<code>*</code>:比對目前目錄層級(不包含子目錄)下的任何内容、任何檔案名或任何檔案擴充名,可以通過<code>/</code>、<code>\</code>和<code>.</code>進行分隔。

<code>**</code>:比對目錄多層級(包含子目錄)的任何内容,用于遞歸比對多層級目錄的多個檔案。

<code>PhysicalFileProvider</code>用于提供實體檔案系統的通路。該提供程式需要将檔案路徑範圍限定在一個目錄及其子目錄中,不能通路目錄外部的内容。

當執行個體化該檔案提供程式時,需要提供一個絕對的目錄路徑,作為檔案目錄的root。

<code>PhysicalFileProvider</code>目錄或檔案路徑不支援glob(通配符)模式。

<code>ManifestEmbeddedFileProvider</code>用于提供嵌入在程式集中的檔案的通路。

可能你對這個嵌入檔案比較陌生,沒關系,請按照下面的步驟來:

安裝Nuget包:<code>Install-Package Microsoft.Extensions.FileProviders.Embedded</code>

編輯<code>.csproj</code>檔案:

添加<code>&lt;GenerateEmbeddedFilesManifest&gt;</code>,并設定為<code>true</code>

使用<code>&lt;EmbeddedResource&gt;</code>添加要嵌入的檔案

以下是 .csproj 檔案的示例:

現在我們通過<code>ManifestEmbeddedFileProvider</code>來提供嵌入到程式集的 files 目錄下檔案的通路:

現在,你可以通過 http://localhost:5000/files/file.json 來通路檔案了。

<code>CompositeFileProvider</code>用于将多種檔案提供程式進行內建。

如:

現在,你可以通過 http://localhost:5000/composite/file.json 來通路檔案了。

Http請求頭中的<code>Content-Type</code>大家一定很熟悉,<code>ContentTypeProvider</code>就是用來提供檔案擴充名和MIME類型映射關系的。

若我們沒有顯示指定<code>ContentTypeProvider</code>,則架構預設使用<code>FileExtensionContentTypeProvider</code>,其實作了接口<code>IContentTypeProvider</code>:

在<code>FileExtensionContentTypeProvider</code>的無參構造函數中,預設添加了380種已知的檔案擴充名和MIME類型的映射,存放在<code>Mappings</code>屬性中。你也可以添加自定義的映射,或移除不想要的映射。

通過<code>UseStaticFiles</code>擴充方法,可以友善的注冊<code>StaticFileMiddleware</code>中間件:

緊接着檢視<code>StaticFileMiddleware</code>的<code>Invoke</code>方法:

通過<code>UseDirectoryBrowser</code>擴充方法,可以友善的注冊<code>DirectoryBrowserMiddleware</code>中間件:

緊接着檢視<code>DirectoryBrowserMiddleware</code>的<code>Invoke</code>方法:

通過<code>UseDefaultFiles</code>擴充方法,可以友善的注冊<code>DefaultFilesMiddleware</code>中間件:

緊接着檢視<code>DefaultFilesMiddleware</code>的<code>Invoke</code>方法:

FileServer并不是某個具體的中間件,它的實作還是依賴了<code>StaticFileMiddleware</code>、<code>DirectoryBrowserMiddleware</code>和<code>DefaultFilesMiddleware</code>這3個中間件。不過,我們可以看一下<code>UseFileServer</code>裡的邏輯:

在接口<code>IHostingEnvironment</code>中,包含<code>ContentRootFileProvider</code>和<code>WebRootFileProvider</code>兩個檔案提供程式。下面我們就看一下他們是如何被初始化的。

使用<code>UseDirectoryBrowser</code>和<code>UseStaticFiles</code>提供檔案浏覽和通路時,URL 受大小寫和基礎檔案系統字元的限制。例如,Windows 不區分大小寫,但 macOS 和 Linux 區分大小寫。

如果使用 IIS 托管應用,那麼 IIS 自帶的靜态檔案處理器是不工作的,均是使用 ASP.NET Core Module 進行處理的,包括靜态檔案處理。

使用<code>UseFileServer</code>擴充方法提供檔案浏覽和通路,其內建了<code>UseStaticFiles</code>、<code>UseDirectoryBrowser</code>和<code>UseDefaultFiles</code>三個中間件的功能。

<code>UseStaticFiles</code>:注冊<code>StaticFilesMiddleware</code>,提供檔案通路

<code>UseDirectoryBrowser</code>:注冊<code>DirectoryBrowserMiddleware</code>,提供檔案目錄浏覽

<code>UseDefaultFiles</code>:注冊<code>DefaultFilesMiddleware</code>,當Url未指定通路的檔案名時,提供預設頁。

檔案提供程式均實作了接口<code>IFileProvider</code>,常用的檔案提供程式有以下三種:

<code>PhysicalFileProvider</code>:提供實體檔案系統的通路

<code>ManifestEmbeddedFileProvider</code>:提供嵌入在程式集中的檔案的通路

<code>CompositeFileProvider</code>:用于将多種檔案提供程式進行內建。

可通過<code>IWebHostingEnvironment</code>擷取<code>ContentRootFileProvider</code>(預設目錄為項目根目錄)和<code>WebRootFileProvider</code>(預設目錄為Web根目錄)。

繼續閱讀