Provedores de arquivos no ASP.NET Core

Por Steve Smith

O ASP.NET Core abstrai o acesso ao sistema de arquivos por meio do uso de provedores de arquivos. Os provedores de arquivos são usados ​​em toda a estrutura do ASP.NET Core. Por exemplo:

Exibir ou baixar código de exemplo (como baixar)

Interfaces de provedor de arquivo

A interface principal é IFileProvider. IFileProvider expõe métodos para:

IFileInfo fornece métodos e propriedades para trabalhar com arquivos:

É possível ler do arquivo usando o método IFileInfo.CreateReadStream.

O aplicativo de amostra FileProviderSample demonstra como configurar um provedor de arquivos em Startup.ConfigureServices para uso em todo o aplicativo por meio da injeção de dependência.

Implementações do provedor de arquivos

A tabela a seguir lista as implementações de IFileProvider.

Implementação Descrição
Provedor de arquivos compostos Usado para fornecer acesso combinado a arquivos e diretórios de um ou mais provedores.
Provedor de arquivos inseridos de manifesto Usado para acessar arquivos inseridos em assemblies.
Provedor de arquivos físicos Usado para acessar os arquivos físicos do sistema.

Provedor de arquivos físicos

O PhysicalFileProvider fornece acesso ao sistema de arquivos físico. O PhysicalFileProvider usa o tipo System.IO.File (para o provedor físico) e define o escopo de todos os caminhos para um diretório e seus filhos. Esse escopo impede o acesso ao sistema de arquivos fora do diretório especificado e seus filhos. O cenário mais comum para criar e usar um PhysicalFileProvider é solicitar um IFileProvider em um construtor por meio de injeção de dependência.

Ao criar uma instância para esse provedor, um caminho de diretório absoluto é necessário e serve como o caminho base para todas as solicitações feitas usando o provedor. Não há suporte para padrões glob no caminho do diretório.

O código a seguir mostra como usar PhysicalFileProvider para obter o conteúdo do diretório e as informações do arquivo:

var provider = new PhysicalFileProvider(applicationRoot);
var contents = provider.GetDirectoryContents(string.Empty);
var filePath = Path.Combine("wwwroot", "js", "site.js");
var fileInfo = provider.GetFileInfo(filePath);

Tipos no exemplo anterior:

  • provider é um IFileProvider.
  • contents é um IDirectoryContents.
  • fileInfo é um IFileInfo.

O provedor de arquivos pode ser usado para percorrer o diretório especificado por applicationRoot ou chamar GetFileInfo para obter as informações de um arquivo. Padrões glob não podem ser passados para o método GetFileInfo. O provedor de arquivos não tem acesso fora do diretório applicationRoot.

O aplicativo de exemplo FileProviderSample cria o provedor no método Startup.ConfigureServices usando IHostEnvironment.ContentRootFileProvider:

var physicalProvider = _env.ContentRootFileProvider;

Provedor de arquivos inseridos de manifesto

O ManifestEmbeddedFileProvider é usado para acessar arquivos inseridos em assemblies. O ManifestEmbeddedFileProvider usa um manifesto compilado no assembly para reconstruir os caminhos originais dos arquivos inseridos.

Para gerar um manifesto dos arquivos inseridos:

  1. Adicione o pacote NuGet Microsoft.Extensions.FileProviders.Embedded ao seu projeto.

  2. Defina a propriedade <GenerateEmbeddedFilesManifest> como true. Especifique os arquivos a serem inseridos com <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>
    

Use padrões glob para especificar um ou mais arquivos a serem inseridos no assembly.

O aplicativo de amostra FileProviderSample cria um ManifestEmbeddedFileProvider e passa o assembly atualmente em execução para seu construtor.

Startup.cs:

var manifestEmbeddedProvider = 
    new ManifestEmbeddedFileProvider(typeof(Program).Assembly);

Sobrecargas adicionais permitem:

  • Especificar um caminho de arquivo relativo.
  • Delimitar os arquivos segundo a data da última modificação.
  • Nomear o recurso inserido que contém o manifesto do arquivo inserido.
Sobrecarga Descrição
ManifestEmbeddedFileProvider(Assembly, String) Aceita um parâmetro de caminho relativo root opcional. Especifique o root para definir o escopo das chamadas de GetDirectoryContents para esses recursos no caminho fornecido.
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) Aceita um parâmetro de caminho relativo root opcional e um parâmetro de data lastModified (DateTimeOffset). A data lastModified tem como escopo a data da última modificação das instâncias de IFileInfo retornadas por IFileProvider.
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) Aceita um caminho relativo root opcional, data de lastModified e parâmetros manifestName. O manifestName representa o nome do recurso inserido que contém o manifesto.

Provedor de arquivos compostos

O CompositeFileProvider combina instâncias de IFileProvider, expondo uma interface única para trabalhar com arquivos de vários provedores. Ao criar o CompositeFileProvider, passe uma ou mais instâncias de IFileProvider para o construtor.

No aplicativo de amostra FileProviderSample, um PhysicalFileProvider e um ManifestEmbeddedFileProvider fornecem arquivos para um CompositeFileProvider registrado no contêiner de serviço do aplicativo. O código a seguir é encontrado no método Startup.ConfigureServices do projeto:

var physicalProvider = _env.ContentRootFileProvider;
var manifestEmbeddedProvider = 
    new ManifestEmbeddedFileProvider(typeof(Program).Assembly);
var compositeProvider = 
    new CompositeFileProvider(physicalProvider, manifestEmbeddedProvider);

services.AddSingleton<IFileProvider>(compositeProvider);

Monitorar as alterações

O método IFileProvider.Watch proporciona um cenário para monitorar um ou mais arquivos ou diretórios quanto a alterações. O método Watch:

  • Aceita uma cadeia de caracteres de caminho de arquivo, que pode usar padrões glob para especificar vários arquivos.
  • Retorna um IChangeToken.

O token de alteração resultante expõe:

  • HasChanged: uma propriedade que pode ser inspecionada para determinar se uma alteração ocorreu.
  • RegisterChangeCallback: chamada quando são detectadas alterações na cadeia de caracteres do caminho especificado. Cada token de alteração chama apenas seu retorno de chamada associado em resposta a uma única alteração. Para permitir o monitoramento constante, use um TaskCompletionSource<TResult> (mostrado abaixo) ou recrie instâncias de IChangeToken em resposta a alterações.

O aplicativo de exemplo WatchConsole grava uma mensagem sempre que um arquivo .txt no diretório TextFiles é modificado:

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");
}

Alguns sistemas de arquivos, como contêineres do Docker e compartilhamentos de rede, podem não enviar notificações de alteração de forma confiável. Defina a variável de ambiente DOTNET_USE_POLLING_FILE_WATCHER como 1 ou true para sondar o sistema de arquivos a cada quatro segundos em relação a alterações.

Padrões glob

Os caminhos do sistema de arquivos usam padrões curinga chamados padrões glob (ou globbing). Especifique grupos de arquivos com esses padrões. Os dois caracteres curinga são * e **:

*
Corresponde a qualquer coisa no nível da pasta atual, qualquer nome de arquivo ou qualquer extensão de arquivo. As correspondências são terminadas pelos caracteres / e . no caminho do arquivo.

**
Coincide a qualquer coisa em vários níveis de diretório. Pode ser usada para fazer a correspondência recursiva com vários arquivos em uma hierarquia de diretórios.

A tabela a seguir fornece exemplos comuns de padrões glob.

Padrão Descrição
directory/file.txt Corresponde a um arquivo específico em um diretório específico.
directory/*.txt Corresponde a todos os arquivos com a extensão .txt em um diretório específico.
directory/*/appsettings.json Corresponde a todos os arquivos appsettings.json em diretórios que estão exatamente um nível abaixo da pasta directory.
directory/**/*.txt Corresponde a todos os arquivos com a extensão .txt encontrados em qualquer lugar abaixo da pasta directory.

O ASP.NET Core abstrai o acesso ao sistema de arquivos por meio do uso de provedores de arquivos. Os provedores de arquivos são usados ​​em toda a estrutura do ASP.NET Core:

Exibir ou baixar código de exemplo (como baixar)

Interfaces de provedor de arquivo

A interface principal é IFileProvider. IFileProvider expõe métodos para:

IFileInfo fornece métodos e propriedades para trabalhar com arquivos:

Você pode ler o arquivo usando o método IFileInfo.CreateReadStream.

O aplicativo de amostra demonstra como configurar um provedor de arquivos em Startup.ConfigureServices para uso em todo o aplicativo por meio da injeção de dependência.

Implementações do provedor de arquivos

Três implementações de IFileProvider estão disponíveis.

Implementação Descrição
PhysicalFileProvider O provedor físico é usado para acessar os arquivos físicos do sistema.
ManifestEmbeddedFileProvider O provedor inserido de manifesto é usado para acessar arquivos incorporados em assemblies.
CompositeFileProvider O provedor composto é usado para fornecer acesso combinado a arquivos e diretórios de um ou mais provedores.

PhysicalFileProvider

O PhysicalFileProvider fornece acesso ao sistema de arquivos físico. O PhysicalFileProvider usa o tipo System.IO.File (para o provedor físico) e define o escopo de todos os caminhos para um diretório e seus filhos. Esse escopo impede o acesso ao sistema de arquivos fora do diretório especificado e seus filhos. O cenário mais comum para criar e usar um PhysicalFileProvider é solicitar um IFileProvider em um construtor por meio de injeção de dependência.

Ao criar uma instância para esse provedor, um caminho de diretório é necessário e serve como o caminho base para todas as solicitações feitas usando o provedor.

O código a seguir mostra como criar um PhysicalFileProvider e usá-lo para obter o conteúdo do diretório e as informações do arquivo:

var provider = new PhysicalFileProvider(applicationRoot);
var contents = provider.GetDirectoryContents(string.Empty);
var fileInfo = provider.GetFileInfo("wwwroot/js/site.js");

Tipos no exemplo anterior:

  • provider é um IFileProvider.
  • contents é um IDirectoryContents.
  • fileInfo é um IFileInfo.

O provedor de arquivos pode ser usado para percorrer o diretório especificado por applicationRoot ou chamar GetFileInfo para obter as informações de um arquivo. O provedor de arquivos não tem acesso fora do diretório applicationRoot.

O aplicativo de amostra cria o provedor na classe Startup.ConfigureServices do aplicativo usando IHostingEnvironment.ContentRootFileProvider:

var physicalProvider = _env.ContentRootFileProvider;

ManifestEmbeddedFileProvider

O ManifestEmbeddedFileProvider é usado para acessar arquivos inseridos em assemblies. O ManifestEmbeddedFileProvider usa um manifesto compilado no assembly para reconstruir os caminhos originais dos arquivos inseridos.

Para gerar um manifesto dos arquivos inseridos, defina a propriedade <GenerateEmbeddedFilesManifest> como true. Especifique os arquivos para inserir com <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>

Use padrões glob para especificar um ou mais arquivos a serem inseridos no assembly.

O aplicativo de amostra cria um ManifestEmbeddedFileProvider e passa o assembly atualmente em execução para seu construtor.

Startup.cs:

var manifestEmbeddedProvider = 
    new ManifestEmbeddedFileProvider(typeof(Program).Assembly);

Sobrecargas adicionais permitem:

  • Especificar um caminho de arquivo relativo.
  • Delimitar os arquivos segundo a data da última modificação.
  • Nomear o recurso inserido que contém o manifesto do arquivo inserido.
Sobrecarga Descrição
ManifestEmbeddedFileProvider(Assembly, String) Aceita um parâmetro de caminho relativo root opcional. Especifique o root para definir o escopo das chamadas de GetDirectoryContents para esses recursos no caminho fornecido.
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) Aceita um parâmetro de caminho relativo root opcional e um parâmetro de data lastModified (DateTimeOffset). A data lastModified tem como escopo a data da última modificação das instâncias de IFileInfo retornadas por IFileProvider.
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) Aceita um caminho relativo root opcional, data de lastModified e parâmetros manifestName. O manifestName representa o nome do recurso inserido que contém o manifesto.

CompositeFileProvider

O CompositeFileProvider combina instâncias de IFileProvider, expondo uma interface única para trabalhar com arquivos de vários provedores. Ao criar o CompositeFileProvider, passe uma ou mais instâncias de IFileProvider para o construtor.

No aplicativo de amostra, um PhysicalFileProvider e um ManifestEmbeddedFileProvider fornecem arquivos para um CompositeFileProvider registrado no contêiner de serviço do aplicativo:

var physicalProvider = _env.ContentRootFileProvider;
var manifestEmbeddedProvider = 
    new ManifestEmbeddedFileProvider(typeof(Program).Assembly);
var compositeProvider = 
    new CompositeFileProvider(physicalProvider, manifestEmbeddedProvider);

services.AddSingleton<IFileProvider>(compositeProvider);

Monitorar as alterações

O método IFileProvider.Watch proporciona um cenário para monitorar um ou mais arquivos ou diretórios quanto a alterações. Watch aceita uma cadeia de caracteres de caminho, que pode usar padrões glob para especificar vários arquivos. Watch retorna um IChangeToken. O token de alteração expõe:

  • HasChanged: uma propriedade que pode ser inspecionada para determinar se uma alteração ocorreu.
  • RegisterChangeCallback: chamada quando são detectadas alterações na cadeia de caracteres do caminho especificado. Cada token de alteração chama apenas seu retorno de chamada associado em resposta a uma única alteração. Para permitir o monitoramento constante, use um TaskCompletionSource<TResult> (mostrado abaixo) ou recrie instâncias de IChangeToken em resposta a alterações.

No aplicativo de amostra, o aplicativo de console WatchConsole é configurado para exibir uma mensagem sempre que um arquivo de texto é modificado:

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");
}

Alguns sistemas de arquivos, como contêineres do Docker e compartilhamentos de rede, podem não enviar notificações de alteração de forma confiável. Defina a variável de ambiente DOTNET_USE_POLLING_FILE_WATCHER como 1 ou true para sondar o sistema de arquivos a cada quatro segundos em relação a alterações.

Padrões glob

Os caminhos do sistema de arquivos usam padrões curinga chamados padrões glob (ou globbing). Especifique grupos de arquivos com esses padrões. Os dois caracteres curinga são * e **:

*
Corresponde a qualquer coisa no nível da pasta atual, qualquer nome de arquivo ou qualquer extensão de arquivo. As correspondências são terminadas pelos caracteres / e . no caminho do arquivo.

**
Coincide a qualquer coisa em vários níveis de diretório. Pode ser usada para fazer a correspondência recursiva com vários arquivos em uma hierarquia de diretórios.

Exemplos de padrão glob

directory/file.txt
Corresponde a um arquivo específico em um diretório específico.

directory/*.txt
Corresponde a todos os arquivos com a extensão .txt em um diretório específico.

directory/*/appsettings.json
Corresponde a todos os arquivos appsettings.json em diretórios que estão exatamente um nível abaixo da pasta diretório.

directory/**/*.txt
Corresponde a todos os arquivos com a extensão .txt encontrados em qualquer lugar abaixo da pasta diretório.