ASP.NET Core 中的文件提供程序
注意
此版本不是本文的最新版本。 对于当前版本,请参阅此文的 .NET 8 版本。
警告
此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 对于当前版本,请参阅此文的 .NET 8 版本。
作者:Steve Smith
ASP.NET Core 通过文件提供程序来抽象化文件系统访问。 在 ASP.NET Core 框架中使用文件提供程序。 例如:
- IWebHostEnvironment 将应用的内容根目录和 Web 根目录作为
IFileProvider
类型公开。 - 静态文件中间件使用文件提供程序来查找静态文件。
- Razor 使用文件提供程序来查找页面和视图。
- .NET Core 工具使用文件提供程序和 glob 模式来指定应该发布哪些文件。
文件提供程序接口
主接口为 IFileProvider。 IFileProvider
公开方法以实现以下目的:
- 获取文件信息 (IFileInfo)。
- 获取目录信息 (IDirectoryContents)。
- 设置更改通知(使用 IChangeToken)。
IFileInfo
提供用于处理文件的方法和属性:
- Exists
- IsDirectory
- Name
- Length(以字节为单位)
- LastModified 日期
可以使用 IFileInfo.CreateReadStream 方法从该文件读取数据。
FileProviderSample
示例应用程序演示了如何在 Startup.ConfigureServices
中配置文件提供程序,以便通过依赖关系注入在整个应用程序中使用。
文件提供程序实现
下表列出了 IFileProvider
的实现。
实现 | 描述 |
---|---|
复合文件提供程序 | 用于提供对一个或多个其他提供程序中的文件和目录的合并访问。 |
清单嵌入的文件提供程序 | 用于访问嵌入在程序集中的文件。 |
物理文件提供程序 | 用于访问系统的物理文件。 |
物理文件提供程序
PhysicalFileProvider 提供对物理文件系统的访问。 PhysicalFileProvider
使用 System.IO.File 类型(适用于物理提供程序)并将所有路径范围限定在一个目录及其子目录中。 此范围界定可防止访问指定目录和子目录之外的文件系统。 创建和使用 PhysicalFileProvider
的最常见方案是通过依赖关系注入请求构造函数中的 IFileProvider
。
直接实例化此提供程序时,必须提供绝对目录路径并将其作为使用提供程序发出的所有请求的基路径。 此目录路径中不支持 glob 模式。
以下代码演示如何使用 PhysicalFileProvider
来获取目录内容和文件信息:
var provider = new PhysicalFileProvider(applicationRoot);
var contents = provider.GetDirectoryContents(string.Empty);
var filePath = Path.Combine("wwwroot", "js", "site.js");
var fileInfo = provider.GetFileInfo(filePath);
前面的示例中的类型:
provider
是IFileProvider
。contents
是IDirectoryContents
。fileInfo
是IFileInfo
。
文件提供程序可用于循环访问 applicationRoot
指定的目录或调用 GetFileInfo
来获取文件信息。 glob 模式无法传递给 GetFileInfo
方法。 该文件提供程序无法访问 applicationRoot
目录外部的任何内容。
FileProviderSample
示例应用程序使用 IHostEnvironment.ContentRootFileProvider 在 Startup.ConfigureServices
方法中创建该提供程序:
var physicalProvider = _env.ContentRootFileProvider;
清单嵌入的文件提供程序
ManifestEmbeddedFileProvider 用于访问嵌入在程序集中的文件。 ManifestEmbeddedFileProvider
使用编译到程序集中的某个清单来重建嵌入的文件的原始路径。
若要生成嵌入文件的清单,请按照以下步骤操作:
将
Microsoft.Extensions.FileProviders.Embedded
NuGet 包添加到项目。将
<GenerateEmbeddedFilesManifest>
属性设置为true
。 指定要使用<EmbeddedResource>
嵌入的文件:<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp3.1</TargetFramework> <GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="3.1.0" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="Resource.txt" /> </ItemGroup> </Project>
使用 glob 模式指定要嵌入到程序集中的一个或多个文件。
FileProviderSample
示例应程序用创建 ManifestEmbeddedFileProvider
并将当前正在执行的程序集传递给其构造函数。
Startup.cs
:
var manifestEmbeddedProvider =
new ManifestEmbeddedFileProvider(typeof(Program).Assembly);
其他重载使你能够:
- 指定相对文件路径。
- 将文件范围限制到上次修改日期。
- 为包含嵌入文件清单的嵌入资源命名。
重载 | 描述 |
---|---|
ManifestEmbeddedFileProvider(Assembly, String) |
接受一个可选的 root 相对路径参数。 指定 root 将对 GetDirectoryContents 的调用范围限制为提供的路径下的那些资源。 |
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) |
接受一个可选的 root 相对路径参数和一个 lastModified 日期 (DateTimeOffset) 参数。 lastModified 日期限制 IFileProvider 返回的 IFileInfo 实例的上次修改日期范围。 |
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) |
接受一个可选的 root 相对路径、lastModified 日期和 manifestName 参数。 manifestName 表示包含清单的嵌入资源的名称。 |
复合文件提供程序
CompositeFileProvider 合并 IFileProvider
实例,以便公开一个接口来处理多个提供程序中的文件。 创建 CompositeFileProvider
时,将一个或多个 IFileProvider
实例传递给其构造函数。
在 FileProviderSample
示例应用程序中,PhysicalFileProvider
和 ManifestEmbeddedFileProvider
向在应用程序的服务容器中注册的 CompositeFileProvider
提供文件。 以下代码可在项目的 Startup.ConfigureServices
方法中找到:
var physicalProvider = _env.ContentRootFileProvider;
var manifestEmbeddedProvider =
new ManifestEmbeddedFileProvider(typeof(Program).Assembly);
var compositeProvider =
new CompositeFileProvider(physicalProvider, manifestEmbeddedProvider);
services.AddSingleton<IFileProvider>(compositeProvider);
监视更改
IFileProvider.Watch 方法提供一种方法来监视一个或多个文件或目录的更改。 Watch
方法:
- 接受文件路径字符串(可以使用 glob 模式指定多个文件)。
- 返回 IChangeToken。
生成的更改令牌会公开以下内容:
- HasChanged:可检查此属性以确定是否已发生更改。
- RegisterChangeCallback:检测到指定的路径字符串发生更改时调用此属性。 每个更改令牌仅调用其关联的回调来响应单个更改。 若要启用持续监视,请使用 TaskCompletionSource<TResult>(如下所示)或重新创建
IChangeToken
实例以响应更改。
每当 TextFiles
目录中的 .txt
文件遭修改时,WatchConsole
示例应用程序都会写入一条消息:
private static readonly string _fileFilter = Path.Combine("TextFiles", "*.txt");
public static void Main(string[] args)
{
Console.WriteLine($"Monitoring for changes with filter '{_fileFilter}' (Ctrl + C to quit)...");
while (true)
{
MainAsync().GetAwaiter().GetResult();
}
}
private static async Task MainAsync()
{
var fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());
IChangeToken token = fileProvider.Watch(_fileFilter);
var tcs = new TaskCompletionSource<object>();
token.RegisterChangeCallback(state =>
((TaskCompletionSource<object>)state).TrySetResult(null), tcs);
await tcs.Task.ConfigureAwait(false);
Console.WriteLine("file changed");
}
某些文件系统(例如 Docker 容器和网络共享)可能无法可靠地发送更改通知。 将 DOTNET_USE_POLLING_FILE_WATCHER
环境变量设置为 1
或 true
以每隔四秒轮询一次文件系统,确认是否发生更改(不可配置)。
glob 模式
文件系统路径使用名为 glob(或通配)模式的通配符模式。 使用这些模式指定文件的组。 两个通配符分别是 *
和 **
:
*
匹配当前文件夹级别的任何内容、任何文件名或任何文件扩展名。 匹配由文件路径中的 /
和 .
字符终止。
**
匹配多个目录级别的任何内容。 可用于以递归方式匹配目录层次结构中的许多文件。
下表提供了 glob 模式的常见示例。
模式 | 描述 |
---|---|
directory/file.txt |
匹配特定目录中的特定文件。 |
directory/*.txt |
匹配特定目录中带有 .txt 扩展名的所有文件。 |
directory/*/appsettings.json |
匹配正好位于 directory 文件夹中下一级目录中的所有 appsettings.json 文件。 |
directory/**/*.txt |
匹配在 directory 文件夹下任何位置都能找到的带 .txt 扩展名的所有文件。 |
ASP.NET Core 通过文件提供程序来抽象化文件系统访问。 在 ASP.NET Core 框架中使用文件提供程序:
- IHostingEnvironment 将应用的内容根目录和 Web 根目录作为
IFileProvider
类型公开。 - 静态文件中间件使用文件提供程序来查找静态文件。
- Razor 使用文件提供程序来查找页面和视图。
- .NET Core 工具使用文件提供程序和 glob 模式来指定应该发布哪些文件。
文件提供程序接口
主接口为 IFileProvider。 IFileProvider
公开方法以实现以下目的:
- 获取文件信息 (IFileInfo)。
- 获取目录信息 (IDirectoryContents)。
- 设置更改通知(使用 IChangeToken)。
IFileInfo
提供用于处理文件的方法和属性:
- Exists
- IsDirectory
- Name
- Length(以字节为单位)
- LastModified 日期
可以使用 IFileInfo.CreateReadStream 方法从文件读取内容。
示例应用演示了如何在 Startup.ConfigureServices
中配置文件提供程序,以便通过依赖关系注入在应用中使用。
文件提供程序实现
IFileProvider
有三种实现。
实现 | 描述 |
---|---|
PhysicalFileProvider | 物理提供程序用于访问系统的物理文件。 |
ManifestEmbeddedFileProvider | 清单嵌入式提供程序用于访问程序集中嵌入的文件。 |
CompositeFileProvider | 复合式提供程序提供对一个或多个其他提供程序中的文件和目录的合并访问。 |
PhysicalFileProvider
PhysicalFileProvider 提供对物理文件系统的访问。 PhysicalFileProvider
使用 System.IO.File 类型(适用于物理提供程序)并将所有路径范围限定在一个目录及其子目录中。 此范围界定可防止访问指定目录和子目录之外的文件系统。 创建和使用 PhysicalFileProvider
的最常见方案是通过依赖关系注入请求构造函数中的 IFileProvider
。
直接实例化此提供程序时,必须提供目录路径并将其作为使用提供程序发出的所有请求的基路径。
下面的代码演示如何创建 PhysicalFileProvider
并用它来获取目录内容和文件信息:
var provider = new PhysicalFileProvider(applicationRoot);
var contents = provider.GetDirectoryContents(string.Empty);
var fileInfo = provider.GetFileInfo("wwwroot/js/site.js");
前面的示例中的类型:
provider
是IFileProvider
。contents
是IDirectoryContents
。fileInfo
是IFileInfo
。
文件提供程序可用于循环访问 applicationRoot
指定的目录或调用 GetFileInfo
来获取文件信息。 该文件提供程序无法访问 applicationRoot
目录外部的任何内容。
示例应用使用 IHostingEnvironment.ContentRootFileProvider 在应用的 Startup.ConfigureServices
类中创建提供程序:
var physicalProvider = _env.ContentRootFileProvider;
ManifestEmbeddedFileProvider
ManifestEmbeddedFileProvider 用于访问嵌入在程序集中的文件。 ManifestEmbeddedFileProvider
使用编译到程序集中的某个清单来重建嵌入的文件的原始路径。
若要生成嵌入的文件清单,请将 <GenerateEmbeddedFilesManifest>
属性设置为 true
。 指定要使用 <EmbeddedResource> 嵌入的文件:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resource.txt" />
</ItemGroup>
</Project>
使用 glob 模式指定要嵌入到程序集中的一个或多个文件。
示例应用创建 ManifestEmbeddedFileProvider
并将当前正在执行的程序集传递给其构造函数。
Startup.cs
:
var manifestEmbeddedProvider =
new ManifestEmbeddedFileProvider(typeof(Program).Assembly);
其他重载使你能够:
- 指定相对文件路径。
- 将文件范围限制到上次修改日期。
- 为包含嵌入文件清单的嵌入资源命名。
重载 | 描述 |
---|---|
ManifestEmbeddedFileProvider(Assembly, String) |
接受一个可选的 root 相对路径参数。 指定 root 将对 GetDirectoryContents 的调用范围限制为提供的路径下的那些资源。 |
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) |
接受一个可选的 root 相对路径参数和一个 lastModified 日期 (DateTimeOffset) 参数。 lastModified 日期限制 IFileProvider 返回的 IFileInfo 实例的上次修改日期范围。 |
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) |
接受一个可选的 root 相对路径、lastModified 日期和 manifestName 参数。 manifestName 表示包含清单的嵌入资源的名称。 |
CompositeFileProvider
CompositeFileProvider 合并 IFileProvider
实例,以便公开一个接口来处理多个提供程序中的文件。 创建 CompositeFileProvider
时,将一个或多个 IFileProvider
实例传递给其构造函数。
在示例应用中,PhysicalFileProvider
和 ManifestEmbeddedFileProvider
向在应用的服务容器中注册的 CompositeFileProvider
提供文件:
var physicalProvider = _env.ContentRootFileProvider;
var manifestEmbeddedProvider =
new ManifestEmbeddedFileProvider(typeof(Program).Assembly);
var compositeProvider =
new CompositeFileProvider(physicalProvider, manifestEmbeddedProvider);
services.AddSingleton<IFileProvider>(compositeProvider);
监视更改
IFileProvider.Watch 方法提供了一个方案来监视一个或多个文件或目录是否发生更改。 Watch
接受路径字符串,它可以使用 glob 模式指定多个文件。 Watch
返回 IChangeToken。 更改令牌会公开以下内容:
- HasChanged:可检查此属性以确定是否已发生更改。
- RegisterChangeCallback:检测到指定的路径字符串发生更改时调用此属性。 每个更改令牌仅调用其关联的回调来响应单个更改。 若要启用持续监视,请使用 TaskCompletionSource<TResult>(如下所示)或重新创建
IChangeToken
实例以响应更改。
在示例应用中,WatchConsole 控制台应用配置为每次修改了文本文件时显示一条消息:
private static PhysicalFileProvider _fileProvider =
new PhysicalFileProvider(Directory.GetCurrentDirectory());
public static void Main(string[] args)
{
Console.WriteLine("Monitoring quotes.txt for changes (Ctrl-c to quit)...");
while (true)
{
MainAsync().GetAwaiter().GetResult();
}
}
private static async Task MainAsync()
{
IChangeToken token = _fileProvider.Watch("quotes.txt");
var tcs = new TaskCompletionSource<object>();
token.RegisterChangeCallback(state =>
((TaskCompletionSource<object>)state).TrySetResult(null), tcs);
await tcs.Task.ConfigureAwait(false);
Console.WriteLine("quotes.txt changed");
}
某些文件系统(例如 Docker 容器和网络共享)可能无法可靠地发送更改通知。 将 DOTNET_USE_POLLING_FILE_WATCHER
环境变量设置为 1
或 true
以每隔四秒轮询一次文件系统,确认是否发生更改(不可配置)。
glob 模式
文件系统路径使用名为 glob(或通配)模式的通配符模式。 使用这些模式指定文件的组。 两个通配符分别是 *
和 **
:
*
匹配当前文件夹级别的任何内容、任何文件名或任何文件扩展名。 匹配由文件路径中的 /
和 .
字符终止。
**
匹配多个目录级别的任何内容。 可用于以递归方式匹配目录层次结构中的许多文件。
glob 模式示例
directory/file.txt
匹配特定目录中的特定文件。
directory/*.txt
匹配特定目录中带 .txt 扩展名的所有文件。
directory/*/appsettings.json
匹配正好位于“目录”文件夹中下一级目录中的所有 appsettings.json
文件。
directory/**/*.txt
匹配在“目录”文件夹下任何位置找到的带 .txt 扩展名的所有文件。