# 第24课：文件提供程序：让你可以将文件放在任何地方

文件提供程序核心类型：

1、IFileProvider

2、IFileInfo

3、IDirectoryContents

IFileProvider 是访问各种各样文件提供程序的接口

通过这样子抽象的定义，让我们与具体的抽象文件的读取的代码进行了隔离

这样的好处是我们可以从不同的地方去读取文件，不仅仅是我们的物理文件，也可以是嵌入式文件，甚至可以说是云端上面的其他 API 提供的文件

内置的提供程序有三种：

（1）PhysicalFileProvider：物理文件的提供程序

（2）EmbeddedFileProvider：嵌入式的提供程序

（3）CompositeFileProvider：组合文件的提供程序

组合文件的提供程序是指当我们有多种文件数据来源的时候，可以将这些源合并为一个目录一样，让我们像在使用同一个目录一样使用我们的文件系统

首先我们可以看一下 IFileProvider 的定义

```
namespace Microsoft.Extensions.FileProviders
{
  public interface IFileProvider
  {
    // 输入是一个相对的路径
    IFileInfo GetFileInfo(string subpath);

    // 获取指定目录下的目录信息
    IDirectoryContents GetDirectoryContents(string subpath);

    IChangeToken Watch(string filter);
  }
}
```

IDirectoryContents

```
namespace Microsoft.Extensions.FileProviders
{
  public interface IDirectoryContents : IEnumerable<IFileInfo>, IEnumerable
  {
    bool Exists { get; }
  }
}
```

这个接口实际上就是 IFileInfo 的一个集合，还有一个属性是否存在，表示当前目录是否存在，如果存在的话，我们可以从它内部枚举到我们的所有文件

IFileInfo

```
namespace Microsoft.Extensions.FileProviders
{
  public interface IFileInfo
  {
    bool Exists { get; }

    long Length { get; }

    string PhysicalPath { get; }

    string Name { get; }

    DateTimeOffset LastModified { get; }

    bool IsDirectory { get; }

    Stream CreateReadStream();
  }
}
```

IFileInfo 有几个属性：是否存在，文件长度，物理地址，文件名，最后修改时间，是否是一个目录（有可能获取到的文件并不是一个真实的文件，它可能是一个目录，那也就是用 IFileInfo 来代替的），读取文件流

接下来通过代码看一下

首先添加 microsoft.extensions.fileproviders 相关 nuget 包引用

```
// 定义一个物理文件的提供程序，把我们当前应用程序的根目录映射出来
IFileProvider provider1 = new PhysicalFileProvider(AppDomain.CurrentDomain.BaseDirectory);

// 获取到这个目录下面的所有内容
var contents = provider1.GetDirectoryContents("/");

foreach (var item in contents)
{
    // 打印文件名
    Console.WriteLine(item.Name);
}
```

启动程序可以看到控制台输出了编译目录下面的文件

```
FileProviderDemo.deps.json
FileProviderDemo.dll
FileProviderDemo.exe
FileProviderDemo.pdb
FileProviderDemo.runtimeconfig.dev.json
FileProviderDemo.runtimeconfig.json
Microsoft.Extensions.FileProviders.Abstractions.dll
Microsoft.Extensions.FileProviders.Composite.dll
Microsoft.Extensions.FileProviders.Embedded.dll
Microsoft.Extensions.FileProviders.Physical.dll
Microsoft.Extensions.FileSystemGlobbing.dll
Microsoft.Extensions.Primitives.dll
```

如果我们要读文件流的话，可以通过 CreateReadStream

```
foreach (var item in contents)
{
    // 读取文件流
    var stream = item.CreateReadStream();
    // 打印文件名
    Console.WriteLine(item.Name);
}
```

接下来看一下嵌入式的提供程序，它是指编译时把文件嵌入到程序集内部，就像源文件一样，但是与通常的资源文件不同的是，我们可以像读取目录一样读取我们的文件

```
IFileProvider provider2 = new EmbeddedFileProvider(typeof(Program).Assembly);
```

这里我们创建了一个 emb.html

```
<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>

</body>
</html>
```

然后把它的属性设置为嵌入的资源，而不是内容

这样的设置的话，我们可以看一下对工程文件有什么影响

编辑项目可以看到我们把这个文件定义为嵌入式资源

```
  <ItemGroup>
    <EmbeddedResource Include="emb.html" />
  </ItemGroup>
```

再次读取这个文件

```
IFileProvider provider2 = new EmbeddedFileProvider(typeof(Program).Assembly);

var html = provider2.GetFileInfo("emb.html");
```

断点调试查看文件信息

![](https://3621256661-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVizOiU5NHZ3U95Qy88Xl%2Fuploads%2F93SjDvA8CbqMGhcqXhct%2F153.png?alt=media\&token=57955efc-cba4-4228-8062-b6b59557cd88)

可以看到 html 这个文件是否存在，是否目录，最后修改时间，长度，名字，物理路径

这就是可以通过嵌入式的文件提供程序来读取编译时构建到程序集里面的资源

最后一个就是组合文件提供程序，它的作用就是将各种提供程序组合成一个目录，让我们可以访问它

```
// 传入前面的两种文件提供程序到组合提供程序里面，它可以传入多个文件提供程序
IFileProvider provider = new CompositeFileProvider(provider1, provider2);

var contents = provider.GetDirectoryContents("/");

foreach (var item in contents)
{
    Console.WriteLine(item.Name);
}
```

启动程序可以看到，不仅输出了程序集，编译构建出来的文件，同时还输出资源文件 emb.html

```
FileProviderDemo.deps.json
FileProviderDemo.dll
FileProviderDemo.exe
FileProviderDemo.pdb
FileProviderDemo.runtimeconfig.dev.json
FileProviderDemo.runtimeconfig.json
Microsoft.Extensions.FileProviders.Abstractions.dll
Microsoft.Extensions.FileProviders.Composite.dll
Microsoft.Extensions.FileProviders.Embedded.dll
Microsoft.Extensions.FileProviders.Physical.dll
Microsoft.Extensions.FileSystemGlobbing.dll
Microsoft.Extensions.Primitives.dll
emb.html
```

这就说明可以像在访问同一个目录一样，访问不同的文件提供程序目录，这就意味着实际上是可以通过实现简单的 IFileProvider 和 IFileInfo 就可以实现自己的文件提供程序

这些文件提供程序举一个场景比如说可以通过 OSS 的这种远程存储的方式将文件读取出来并且提供给应用程序，但是应用程序并不需要做特殊的配置，只需要把 OSS 提供的程序注入到系统里面，只需要按照 IFileProvider 提供的接口来读取文件，就可以做到像在读取本地文件一样，也就是说可以借助这套框架读取任意位置的文件

GitHub源码链接：<https://github.com/MingsonZheng/DotNetCoreDevelopmentActualCombat/tree/main/FileProviderDemo>
