ASP.NET Core 文件系統
緊接上一講 中間件 之后,今天來我們來講一下關於 ASP.NET Core 中靜態文件服務。
什么是靜態文件?
先看一下下面例子(在客戶端瀏覽器中通過 url 路徑訪問了網站的一張圖片):
這樣的圖片就是一個靜態文件
-
靜態文件(staticfiles),其實就是指像 HTML、CSS、圖片 和 JavaScript 之類固定的文件,是會被應用程序直接提供給客戶端的資源。
-
靜態文件通常位於web root(內容根目錄或Web根目錄)文件夾下。有關 內容根目錄 和 Web根目錄 的信息在第一講中介紹過。
- 內容根目錄(Contentroot):是應用程序所用到的所有內容的根路徑。WebHost.CreateDefaultBuilder() 中默認下把項目的當前目錄設置為內容根目錄,也就是指 web 的項目的文件夾,包括 bin 和 webroot 文件夾。
- Web根目錄(webroot):項目中用於存放類似於 CSS、JS、圖片等公開、靜態資源的目錄。
- Web根目錄默認為的是 Contentroot 路徑下的 wwwroot 文件夾。
- 靜態文件中間件將默認只讀取 Web根目錄和其子目錄中的文件。
- 對於 Razor (.cshtml) 文件,波浪號斜杠 ~/ 指向 webroot。 以 ~/ 開頭的路徑稱為虛擬路徑。
-
靜態文件可以保存在 Web根目錄(默認為wwwroot) 下的任意文件夾內,並通過相對根的路徑來訪問。例如當你通過 VisualStudio 創建一個默認的 Web應用程序(MVC)項目時,在 wwwroot 目錄下會多出幾個文件夾:css、images以及js。
通過下面的URL就能夠直接訪問 images 目錄下的圖片:
http://<app>/images/<imageFileName>
http://localhost:7819/images/banner1.svg
靜態文件中間件
如上例子,為了能夠使用靜態文件的服務,必須配置中間件,把靜態文件中間件加入到請求管道內。(默認創建的 MVC Web應用程序已經配置了)
靜態文件中間件可通過下述方法來配置:在項目中增加 MicrosoftAspNetCore.StaticFiles 包依賴,然后從Startup.Configure 中調用 app.UseStaticFiles 擴展方法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseStaticFiles();
}
上面我們通過一個簡單的實例使用 app.UseStaticFiles() 來使用文件中間件,使得Web根目錄下的所有靜態文件直接發布出來。如果我們需要發布的靜態文件存儲在其他目錄下呢?依舊是這個應用,現在我們將靜態文件放在自定義文件夾 MyStaticFiles 內,並將以Web的形式發布出來,展示如何通過使用 UseStaticFiles 將其他非wwwroot目錄下的內容也向外提供服務。
如果要訪問 MyStaticFiles 文件夾下的 test.png 圖片,可以這樣配置靜態文件中間件:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions() // 使用 StaticFileOptions 這個對象來初始化靜態文件中間件
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")), // 物理文件路徑
RequestPath = new PathString("/StaticFiles"), // 請求路徑
});
}
ASP.NET Core 一般情況下都是利用一個FileProvider 對象來讀取文件的,它在處理針對靜態文件的請求是也不例外。
上邊代碼在靜態文件中間件中,在調用靜態文件中間件的方法 UseStaticFiles(),該方法可接受一個配置項對象 StaticFileOptions,其內部具有一個 FileProvider 和請求路徑的映射關系。
如果調用 UseStaticFiles方法沒有指定任何的參數,那么這個映射關系的請求路徑就是應用的基地址(PathBase),而FileProvider自然就是指向WebRoot目錄的PhysicalFileProvider。
通過訪問 http://<app>/StaticFiles/test.png,這樣就能訪問到 test.png 圖片文件了。
靜態文件授權 :
靜態文件中間件默認不提供授權檢查。任何通過該模塊提供訪問的文件,包括位丁 wwwroot 下的文件都是公開的,那么如何給文件提供授權呢?
將文件保存在 wwwroot 之外,並將目錄設置為可被靜態文件中間件訪問到,同時通過一個 controller action 來訪問,通過授權后返回HleResulU將文件保存在wwwroot之外,並將目錄設置為可被靜態文件中間件訪問到,同時通過一個itrolleraction來訪問,通過授權后返回 FileResult。
目錄瀏覽
以上通過注冊的 StaticFileMiddleware 只會處理針對某個具體靜態文件的請求,如果我們向某個目錄的URL發送HTTP請求,得到的將是一個狀態為404的響應。那是因為基於安全考慮,默認情況下應用程序是禁用目錄訪問功能的。目錄瀏覽就是允許網站用戶看到指定目錄下的目錄和文件列表。我們可以通過注冊一個名為DirectoryBrowserMiddleware 的中間件來顯示請求目錄的內容:
Step1. 在Startup.ConfigureServices 中調用 AddDirectoryBrowser 擴展方法添加目錄瀏覽服務:
public void ConfigureServices(IServiceCollection services) {
services.AddDirectoryBrowser();
}
Step2. 然后在 Startup.Configure 中調用 UseDirectoryBrowser 擴展方法使用中間件來開啟網絡應用目錄瀏覽:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
app.UseStaticFiles();
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = new PathString("/StaticFiles"),
});
app.UseDirectoryBrowser(new DirectoryBrowserOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = new PathString("/StaticFiles"),
});
}
運行程序后,就可以通過訪問 http:<app>/StaticFiles 來瀏覽 ContentRoot/StaticFiles 文件夾中的目錄了。這個中間件會返回一個HTML頁面,將請求目錄下的所有文件將以表格的形式包含在這個頁面中(包括該文件夾下的每一個文件夾與文件)
注意:使用靜態文件和目錄瀏覽的區別,上述Step2.Startup.Configure方法中調用了兩個 app.UseStaticFiles :第一個調用允許請求 wwwroot 文件夾下的靜態文件,第二個調用則是允許通過 http:<app>/StaticFiles/<fileName> 請求 StaticFiles 文件夾中的靜態文件,調用 app.UseDirectoryBrowser 則是允許通過 http:<app>/StaticFiles 請求瀏覽 StaticFiles 文件夾的目錄。
注冊指定目錄的瀏覽
app.UseDirectoryBrowser 方法可接受一個配置項對象 DirectoryBrowserOptions,該對象與靜態文件中間件配置項對象相似,通過該對象可以配置允許用戶瀏覽的目錄和請求的虛擬路徑。
默認文件
設置默認首頁能給站點的每個訪問者提供一個默認起始頁。為了使站點能夠提供默認頁面,避免用戶輸入完整URL,我們可以在 Startup.Configure 中調用 app.UseDefaultFiles 擴展方法:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
app.UseDefaultFiles();
app.UseStaticFiles();
}
app.UseDefaultFiles 必須是在 UseStaticFiles 之前調用。UseDefaultFiles只是重寫了URL,而不是真的提供了這樣一個文件。因此必須開啟靜態文件中間件(UseStaticFiles)來提供這個文件。
通過 UseDefanltFiles,請求文件夾的時候將檢索以下文件:
- default.htm
- default.html
- index.htm
- index.html
列表中第一個被找到的文件將返回給用戶,作為該完整URL的請求的應答,而此時瀏覽器上URL將繼續顯示用戶輸入的URI。
指定默認文件
靜態文件中間件可以接受一個 DefaultFilesOptions 的配置對象參數,通過添加默認主頁名稱,可以指定自定義的默認文件(默認首頁),下面的代碼展示如何將默認文件名改為 myindex.html
public void Configure(IApplicationBuilder app)
{
DefaultFilesOptions options = new DefaultFilesOptions();
options.DefaultFileNames.Clear(); // 清除默認文件
options.DefaultFileNames.Add("login.html"); //添加指定文件為默認文件
app.UseDefaultFiles(options); //使用默認文件中間件
app.UseStaticFiles(); //使用靜態文件中間件(必須)
}
在 web根目錄中添加好 login.html 頁面,運行程序:
UseFileServer
UseFileServer 集合了 UseStaticFiles、UseDefaultFiles以及UseDirectoryBrowser。
下面的代碼啟用了靜態文件和默認文件,但不允許直接訪問目錄:
app.UseFileServer();
下面的代碼啟用了靜態文件、默認文件和目錄瀏覽功能:
app.UseFileServer(enableDirectoryBrowsing:true);
作為一個集合了UseStaticFiles、UseDefaultFiles和UseDirectoryBrowser方法於一體的方法,如果希望提供 web根目錄之外存在的文件,則需要實例化並配置一個 FileServerOptions 對象傳遞給app.UscFileServer 的參數。例如,在應用中有如下層次的目錄:
對於上面這個層次結構的示例,你可能需耍啟用靜態文件、默認文件以及瀏覽 MyStaticFiles 目錄等功能。下面的代碼片段演示了如何通過調用一次 FileServerOptions 來完整實現這些功能:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) {
app.UseFileServer(new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = new PathString("/StaticFiles"),
EnableDirectoryBrowsing = true,
EnableDefaultFiles = true,
});
}
如果將 enableDirectoryBrowsing設置為true,則必須要在 Startup.ConfigureServices 中調用 services.AddDirectoryBrowser 擴展方法:
public void ConfigureServices(IServiceCollection services) {
services.AddDirectoryBrowser();
}
如果在 MyStaticFiles 目錄下存在默認頁面,則打開默認頁面。如果沒有默認命名的文件,則http://StaticFiles將返回目錄 StaticFiles 的目錄列表,如下圖所示。
FileExtensionContentTypeProvider
FileExtensionContentTypeProvider 類內包含一個將文件擴展名映射到 MIME內容類型的集合。在下面的例子中,將多個文件擴展名(如:.myapp)注冊為已知的MIME類型(application/x-msdownload),“.rtf”被替換,“.mp4”被移除:
public void Configure(IApplicationBuilder app) {
FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider();
// add new mapping.
provider.Mappings[".myapp"] = "application/x-msdownload";
provider.Mappings[".htm3"] = "text/html";
provider.Mappings[".image"] = "image/png";
// replace an existing mepping.
provider.Mappings[".rtf"] = "application/x-msdownload";
// remove mp4 vidios.
provider.Mappings.Remove(".mp4");
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = new PathString("/StaticFiles"),
ContentTypeProvider = provider,
});
app.UseDirectoryBrowser(new DirectoryBrowserOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = new PathString("/StaticFiles"),
});
}
非標准的內容類型
ASP.NET Core 靜態文件中間件能夠支持超過400多種己知的文件內容類型。如果用戶請求一個未知的文件類型,靜態文件中間件將返 HTTP404(未找到)響應。如果啟用目錄瀏覽,則該文件的URL將會被顯示,但訪問URL會返回一個 HTTP404 錯誤。下面則通過代碼把不能識別的類型和文件作為下載的文件處理:
public void Configure(IApplicationBuilder app) {
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = new PathString("/StaticFiles"),
ServeUnknownFileTypes = true,
DefaultContentType = "application/x-msdownload",
});
app.UseDirectoryBrowser(new DirectoryBrowserOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
RequestPath = new PathString("/StaticFiles"),
});
}
注意事項:
- UseDirectoryBrowser 和 UseStaticFiles 可能會泄密。建議不要在生產環境開啟目錄瀏覽。要小心那些被你開啟了 UseStaticFiles 或 UseDirectoryBrowser 的目錄,它們的子文件及子目錄都可被訪問。建議將公開內容放在<content root>/wwwroot 這樣的目錄中,遠離應用程視圖、配置文件等。
- 使用 UseDirectoryBrowser 和 UseStaticFiles 暴露的文件的 URL是否區分大小寫以及字符限制,受制於底層文件系統。比如Windows是不區分大小寫的,但MACOS和Linux則區分大小寫。
實戰簡單文件服務器
① 首先新建一個ASP.NETCore項目,選擇空的模板。
② 使用NuGet命令添加Microsoft.AspNetCore.StaticFiles引用:
Install-PackageMicrosoft.AspNetCore.StaticFiles
③ 如果我們的文件服務器需要能訪問和瀏覽E盤的所有文件與文件夾,那么我們就需要用到 UseStaticFiles 和 UseDirectoryBrowser 方法了,在 Startup.Configure 方法下添加如下代碼:
public void Configure(IApplicationBuilder app) {
FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider();
provider.Mappings[".log"] = "text/plain";
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(@"E:\"), // 指定靜態文件目錄 ServeUnknownFileTypes = true, ContentTypeProvider = provider, DefaultContentType = "application/x-msdownload", // 設置未識別的MIME類型一個默認z值 }); app.UseDirectoryBrowser(new DirectoryBrowserOptions() { FileProvider = new PhysicalFileProvider(@"E:\"), // 制定目錄,這里指定 E盤,也可以是其他目錄 }); }
然后運行程序,這里選擇使用 Kestrel,訪問:http://<ip>:5000/,如下圖所示:
這樣我們就能通過客戶端瀏覽服務器E盤的所有文件及文件夾了。我們還手動設置了未識別的 MIME 類型一個默認值("application/x-msdownload"),當瀏覽器打開這些未識別類型的文件,就會下載這些文件。像 .log 這樣的文件就被手動設置為文本方式,瀏覽器會直接展示出來。這樣我們也就實現了一個簡單的文件服務器。
如果想用局域網內其他電腦或手機查看圖片或視頻文件,則修改Properties文件下 launchSettings.json 配置文件中的應用程序URL就可以實現:
"WebApplication": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://*:5001;http://*:5000", // 這樣就可以通過 http:ip:5000訪問
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
這樣在同一局域網內,在手機上訪問 http:ip:5000,就能訪問服務器E盤中的文件了。
參考原文
Microsoft 文檔 ASP.NET Core 中間件
ASP.NET Core 中間件(Middleware)詳解
