ASP.NET Core File Providers

原文地址:FileProvider

By Steve Smith

ASP.NET Core通过对File Providers的使用实现了对文件系统访问的抽象。

查看或下载示例代码

File Provider 抽象

File Providers是文件系统之上的一层抽象。它的主要接口是IFileProviderIFileProvider公开了相应方法用来获取文件信息(IFileInfo), 目录信息(IDirectoryContents),以及设置更改通知(通过使用一个IChangeToken)。

IFileInfo接口提供了操作单个文件和目录的方法和属性。它有两个boolean属性,ExistsIsDirectory,以及两个描述文件的两个属性NameLength(按字节),还包括一个LastModified日期属性。你还可以通过CreateReadStream方法读取文件内容。

File Provider 实现

有三种对于IFileProvider的实现可供选择:物理式,嵌入式和复合式。物理式用于访问实际系统中的文件。嵌入式用于访问嵌入在程序集中的文件。 复合式则是对前两种方式的组合使用。

PhysicalFileProvider

PhysicalFileProvider提供了对物理文件系统的访问。它封装了System.IO.File类型,范围限定到一个目录及其子目录的所有路径。这类作用域会限制访问某个目录及其子目录,防止作用域以外的其他操作访问文件系统。当实例化此类provider时,你必须为它提供一个目录路径,以供服务器拿来当做由这个provider发出的所有请求的基础路径(这个provider会限制路径以外的访问请求)。在一个ASP.NET Core应用,你可以直接实例化出一个PhysicalFileProvider provider,或者你也可以通过在控制器和服务中使用构造函数依赖注入的方式,请求一个IFileProvider接口。后者生成的解决方案通常更灵活以及更便于测试。

要创建一个PhysicalFileProvider其实很简单,只需要对其实化,再传递给它一个物理路径。之后你就可以通过它的目录遍历内容或提供子路径获取特定文件的信息。

IFileProvider provider = new PhysicalFileProvider(applicationRoot);
IDirectoryContents contents = provider.GetDirectoryContents(""); // the applicationRoot contents
IFileInfo fileInfo = provider.GetFileInfo("wwwroot/js/site.js"); // a file under applicationRoot

为了在控制器中请求一个provider,需要在控制器的构造函数中指定类型参数并赋值给本地属性。之后你就可以在你的动作器方法中使用本地实例了。

public class HomeController : Controller
{
    private readonly IFileProvider _fileProvider;

    public HomeController(IFileProvider fileProvider)
    {
        _fileProvider = fileProvider;
    }

    public IActionResult Index()
    {
        var contents = _fileProvider.GetDirectoryContents("");
        return View(contents);
    }
}

在应用的Startup类中创建provider的代码如下:

using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;

namespace FileProviderSample
{
    public class Startup
    {
        private IHostingEnvironment _hostingEnvironment;
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();

            _hostingEnvironment = env;
        }

        public IConfigurationRoot Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();

            var physicalProvider = _hostingEnvironment.ContentRootFileProvider;
            var embeddedProvider = new EmbeddedFileProvider(Assembly.GetEntryAssembly());
            var compositeProvider = new CompositeFileProvider(physicalProvider, embeddedProvider);

            // choose one provider to use for the app and register it
            //services.AddSingleton<IFileProvider>(physicalProvider);
            //services.AddSingleton<IFileProvider>(embeddedProvider);
            services.AddSingleton<IFileProvider>(compositeProvider);
        }
    }
}

Index.chhtml 视图中,可以遍历操作IDirectoryContents模型参数

@using Microsoft.Extensions.FileProviders
@model  IDirectoryContents

<h2>Folder Contents</h2>

<ul>
    @foreach (IFileInfo item in Model)
    {
        if (item.IsDirectory)
        {
            <li><strong>@item.Name</strong></li>
        }
        else
        {
            <li>@item.Name - @item.Length bytes</li>
        }
    }
</ul>

结果如下:

EmbeddedFileProvider

EmbeddedFileProvider用于访问嵌入到程序集中的文件。在.NET Core中,你可以通过修改 project.json 文件的buildOptions属性参数来把文件嵌入到程序集中。

"buildOptions": {
  "emitEntryPoint": true,
  "preserveCompilationContext": true,
  "embed": [
    "Resource.txt",
    "**/*.js"
  ]
}

当你把文件嵌入到程序集中时,你可以使用通配符模式。这些模式可以被用来匹配一个或多个文件。

Note 把项目中所有的.js文件都嵌入到项目程序集里的情况是不太可能发生的,以上示例仅作为demo给出。

当创建一个EmbeddedFileProvider时,请在其构造函数中传入一个程序集实例供其读取。

var embeddedProvider = new EmbeddedFileProvider(Assembly.GetEntryAssembly());

以上的代码片段描述了如何创建一个能访问当前工作程序集的EmbeddedFileProvider类型变量。

使用EmbeddedFileProvider更新示例项目代码后的输出结果如下:

Note 如上图所示,嵌入式资源不会公开目录。相反的,资源路径(经由资源的命名空间)会被嵌入到它的文件名中并以.作为分隔符。

Tip EmbeddedFileProvider构造器接受一个可选的baseNamespace参数,指定此参数将限定GetDirectoryContents方法调用该命名空间下的资源。

CompositeFileProvider

CompositeFileProvider联合IFileProvider实例公开了一个单一的接口,用以和来自多种provider的文件工作。当创建一个CompositeFileProvider时,你可以为它的构造函数传入一个或多个IFileProvider实例。

var physicalProvider = _hostingEnvironment.ContentRootFileProvider;
var embeddedProvider = new EmbeddedFileProvider(Assembly.GetEntryAssembly());
var compositeProvider = new CompositeFileProvider(physicalProvider, embeddedProvider);

使用包含物理式provider(在前)和嵌入式provider的CompositeFileProvider更新示例项目代码后的输出结果如下:

查看更改

IFileProviderWatch方法能用来查看一个或多个文件/目录的更改信息。Watch方法接受一个路径字符串,它也可以使用通配符模式来指定多个文件,Watch方法最终返回一个IChangeToken。这个token公开了一个HasChanged属性用以检视状态,公开了一个RegisterChangeCallback方法,此方法会在指定的路径字符串检测到更改时被调用。请注意每个更改token只调用其关联回调以响应单次更改。为了使监控持续,你可以使用如下所示的TaskCompletionSource方法,或者重建IChangeToken以响应更改。

在这个文章的示例中,无论何时当文本文件内容发生修改,按如下代码配置的console应用都会显示相应的信息。

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;

namespace WatchConsole
{
    public class Program
    {
        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");
        }
    }
}

以下是执行过几次文本保存动作后的运行结果截图:

Note 有一些文件系统,例如Docker容器和网络共享,可能不能很可靠地发送更改通知。设置环境变量DOTNET_USE_POLLINGFILEWATCHER的值为1true,使得每四秒轮询一次文件系统的变更。

通配符模式

文件系统路径规则使用叫作globbing patterns的通配符模式,这类简单模式可以被用来指定文件组。这两个通配符分别是***

* *表示在当前文件夹级别上匹配任何文件名称或文件扩展名。匹配以文件路径字符串中的/.符号结尾。

** **表示在多个目录级别上匹配任何文件名称或文件扩展名。可用于在一个目录层次结构中递归地匹配多个文件。

通配符模式示例

directory/file.txt

在指定的文件夹中匹配指定的文件。

directory/*.txt

在指定的文件夹中匹配所有以.txt扩展名结尾的文件。

directory/*/project.json

在指定的directory文件夹下的一级目录位置中匹配所有符合project.json名称的文件

directory/**/*.txt

在指定的directory文件夹下的所有位置中匹配所有以.txt扩展名结尾的文件。

在ASP.NET Core中File Provider的用法

ASP.NET Core有几个组件使用file provider功能。IHostingEnvironmentIFileProvider接口类型公开了应用的目录根和Web根。静态文件中间件使用file provider来定位静态文件。Razor更是大量使用IFileProvider来定位视图。Dotnet的发布功能使用file provider和通配符模式来指定需要跟随发布的文件。

在应用程序中使用的建议

如果你的ASP.NET Core应用需要访问文件系统,你可以通过依赖注入创建IFileProvider接口实例,然后再通过前文所示的相应方法执行访问。当应用启动的时候,这些方法允许你一次性配置provider并减少应用初始化时生成的实例类型数目。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏代码散人

如何使用Swift Package Manager那么,让我门开始吧

Swift Package Manager 是苹果推出的用于创建使用swift的库和可执行程序的工具。

1674
来自专栏hrscy

iOS 多线程--NSThread

比如售票问题,同一张票在某个时间只能卖给一个人,不能把一张票卖给多个人,这样就会出问题。

912
来自专栏Core Net

ASP.NET Core 2.1 : 十二.内置日志、使用Nlog将日志输出到文件

2142
来自专栏王磊的博客

GentleNet使用之详细图解[语法使用增强版]

目录 第一章 开发环境 第二章 简介 第三章 Gentle.Net-1.5.0 下载文件包介绍 第四章 使用步骤 第五章 源码下载 第一章、开发环境: Vs 2...

2849
来自专栏游戏开发那些事

【Linux程序设计】之进程间的通信

这个系列的博客贴的都是我大二的时候学习Linux系统高级编程时的一些实验程序,都挺简单的。

923
来自专栏Android 研究

Android插件化基础3----Android的编译打包流程详解

.apk文件其实就是一个压缩包,把文件的后缀改成.zip,用压缩软件解压搜就可的下图(我是mac)

1911
来自专栏NetCore

保护连接字符串

保护连接字符串 摘自MSDN 保护对数据源的访问是安全应用程序最重要的目标之一。为了帮助限制对数据源的访问,必须保护连接信息(例如用户标识、密码和数据源名称)的...

2015
来自专栏逸鹏说道

[开源] 分享导出博客园文章成本地 Markdown 文件存储的工具

此文主要分享了如何将自己博客园的文章自动导出到 Markdown 文档进行存储,以便在本地进行归档管理,程序中也对文章的分类、tag、代码块以及文章中的图片进行...

3945
来自专栏大内老A

谈谈你最熟悉的System.DateTime[下篇]

在《上篇》中,我们实现了将保存有消息条目的XML向CodeDOM的转换,即是将XML文件生成一个CodeCompileUnit对象,而该CodeCompileU...

1785
来自专栏程序员互动联盟

【答疑释惑第三十六讲】Windows下如何调试?

疑惑一 Windows窗口程序到底能不能用printf? 很多小伙伴在控制台下写程序时,要打印或者调试很方便,用printf就可以直接打印输出,方便看结果,但是...

2694

扫码关注云+社区

领取腾讯云代金券