注:本文隸屬于《了解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><GenerateEmbeddedFilesManifest></code>,并設定為<code>true</code>
使用<code><EmbeddedResource></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根目錄)。