Compartir vía


Proveedores de archivo en ASP.NET Core

Por Steve Smith

ASP.NET Core abstrae el acceso al sistema de archivos mediante el uso de proveedores de archivos. Los proveedores de archivos se usan en el marco de ASP.NET Core. Por ejemplo:

Vea o descargue el código de ejemplo (cómo descargarlo)

Interfaces de proveedor de archivos

La interfaz principal es IFileProvider. IFileProvider expone métodos para:

IFileInfo proporciona métodos y propiedades para trabajar con archivos:

Se puede leer en el archivo mediante el método IFileInfo.CreateReadStream.

La aplicación de ejemplo FileProviderSample muestra cómo configurar un proveedor de archivos en Startup.ConfigureServices para su uso en toda la aplicación a través de la inserción de dependencias.

Implementaciones del proveedor de archivos

En la tabla siguiente se enumeran las implementaciones de IFileProvider.

Implementación Descripción
Proveedor de archivos compuestos Se usa para proporcionar acceso combinado a archivos y directorios de uno o más proveedores.
Proveedor de archivos incrustados de manifiesto Se usa para tener acceso a archivos insertados en ensamblados.
Proveedor de archivos físicos Se usa para tener acceso a los archivos físicos del sistema.

Proveedor de archivos físicos

PhysicalFileProvider proporciona acceso al sistema de archivos físico. PhysicalFileProvider usa el tipo System.IO.File (para el proveedor físico) y define el ámbito de todas las rutas de acceso a un directorio y sus elementos secundarios. Esta definición de ámbito impide el acceso al sistema de archivos fuera del directorio especificado y sus elementos secundarios. El escenario más común para crear y usar PhysicalFileProvider es solicitar IFileProvider en un constructor mediante la inyección de dependencias.

Al crear una instancia de este proveedor directamente, se requiere una ruta de acceso absoluta al directorio, que actúa como la ruta de acceso base para todas las solicitudes realizadas usando el proveedor. Los patrones globales no se admiten en la ruta de acceso al directorio.

El código siguiente muestra cómo usar PhysicalFileProvider para obtener el contenido del directorio y la información del archivo:

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 del ejemplo anterior:

  • provider es IFileProvider.
  • contents es IDirectoryContents.
  • fileInfo es IFileInfo.

El proveedor de archivos puede usarse para iterar por el directorio especificado por applicationRoot o llamar a GetFileInfo para obtener información de un archivo. No se pueden pasar patrones globales al método GetFileInfo. El proveedor de archivos no tiene acceso fuera del directorio applicationRoot.

La aplicación de ejemplo FileProviderSample crea el proveedor en el método Startup.ConfigureServices con IHostEnvironment.ContentRootFileProvider:

var physicalProvider = _env.ContentRootFileProvider;

Proveedor de archivos incrustados de manifiesto

ManifestEmbeddedFileProvider se utiliza para acceder a archivos insertados dentro de ensamblados. ManifestEmbeddedFileProvider utiliza un manifiesto compilado en el ensamblado para reconstruir las rutas de acceso originales de los archivos incrustados.

Para generar un manifiesto de los archivos incrustados:

  1. Agregue el paquete NuGet Microsoft.Extensions.FileProviders.Embedded al proyecto.

  2. Establezca la propiedad <GenerateEmbeddedFilesManifest> en true. Especifique los archivos que desea incrustar con <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 patrones globales para especificar uno o varios archivos para incrustar en el ensamblado.

La aplicación de ejemplo FileProviderSample crea un ManifestEmbeddedFileProvider y pasa el ensamblado que se está ejecutando actualmente a su constructor.

Startup.cs:

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

Las sobrecargas adicionales le permiten:

  • Especificar una ruta de acceso de archivo relativa.
  • Definir el ámbito de archivos a la fecha de la última modificación.
  • Asignar nombre al recurso incrustado que contiene el manifiesto del archivo incrustado.
Sobrecarga Descripción
ManifestEmbeddedFileProvider(Assembly, String) Acepta parámetro de ruta de acceso relativa root opcional. Especifique root para definir el ámbito de las llamadas en GetDirectoryContents en aquellos recursos que se encuentran bajo las rutas de acceso proporcionadas.
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) Acepta un parámetro de ruta de acceso relativa root opcional y un parámetro de fecha lastModified (DateTimeOffset). La fecha lastModified define el ámbito de la última fecha de modificación para las instancias de IFileInfo que IFileProvider devuelve.
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) Acepta una ruta de acceso relativa root opcional, una fecha lastModified y parámetros manifestName. manifestName representa el nombre del recurso incrustado que contiene el manifiesto.

Proveedor de archivos compuestos

CompositeFileProvider combina instancias de IFileProvider, y expone una única interfaz para trabajar con archivos de varios proveedores. Al crear CompositeFileProvider, se pasan una o varias instancias de IFileProvider a su constructor.

En la aplicación de ejemplo FileProviderSample, un PhysicalFileProvider y un ManifestEmbeddedFileProvider proporcionan archivos a un CompositeFileProvider registrado en el contenedor de servicios de la aplicación. El código siguiente se encuentra en el método Startup.ConfigureServices del proyecto:

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

services.AddSingleton<IFileProvider>(compositeProvider);

Observación de cambios

El método IFileProvider.Watch proporciona un escenario para ver uno o más archivos o directorios para detectar cambios. El método Watch realiza las acciones siguientes:

  • Acepta una cadena de ruta de acceso a archivo, que puede usar patrones globales para especificar varios archivos.
  • Devuelve un valor IChangeToken.

El token de cambio resultante expone:

  • HasChanged: una propiedad que se puede inspeccionar para determinar si se ha producido un cambio.
  • RegisterChangeCallback: se llama cuando se detectan cambios en la cadena de ruta de acceso especificada. Cada token de cambio solo llama a su devolución de llamada asociada en respuesta a un único cambio. Para habilitar la supervisión constante, puede usar TaskCompletionSource<TResult> (como se muestra a continuación) o volver a crear instancias de IChangeToken en respuesta a los cambios.

La aplicación de ejemplo WatchConsole escribe un mensaje cada vez que se modifica un archivo .txt en el directorio TextFiles:

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

Algunos sistemas de archivos, como contenedores de Docker y recursos compartidos de red, no pueden enviar notificaciones de cambio de forma confiable. Establezca la variable de entorno DOTNET_USE_POLLING_FILE_WATCHER en 1 o true para sondear el sistema de archivos en busca de cambios cada cuatro segundos (no configurable).

Patrones globales

Las rutas de acceso del sistema de archivos utilizan patrones de caracteres comodín denominados patrones globales. Especifique grupos de archivos con estos patrones. Los dos caracteres comodín son * y **:

*
Coincide con cualquier elemento del nivel de carpeta actual, con cualquier nombre de archivo o con cualquier extensión de archivo. Las coincidencias finalizan con los caracteres / y . en la ruta de acceso de archivo.

**
Coincide con cualquier elemento de varios niveles de directorios. Puede usarse para coincidir de forma recursiva con muchos archivos dentro de una jerarquía de directorios.

En la tabla siguiente se proporcionan ejemplos comunes de patrones globales.

Modelo Descripción
directory/file.txt Coincide con un archivo concreto en un directorio específico.
directory/*.txt Coincide con todos los archivos que tengan la extensión .txt en un directorio específico.
directory/*/appsettings.json Coincide con todos los archivos appsettings.json que estén en directorios exactamente un nivel por debajo de la carpeta directory.
directory/**/*.txt Coincide con todos los archivos con una extensión .txt y que se encuentran en cualquier lugar de la carpeta directory.

ASP.NET Core abstrae el acceso al sistema de archivos mediante el uso de proveedores de archivos. Los proveedores de archivos se usan en el marco de ASP.NET Core:

Vea o descargue el código de ejemplo (cómo descargarlo)

Interfaces de proveedor de archivos

La interfaz principal es IFileProvider. IFileProvider expone métodos para:

IFileInfo proporciona métodos y propiedades para trabajar con archivos:

Puede leer del archivo mediante el método IFileInfo.CreateReadStream.

La aplicación de ejemplo muestra cómo configurar un proveedor de archivos en Startup.ConfigureServices para su uso en toda la aplicación a través de la inserción de dependencias.

Implementaciones del proveedor de archivos

Hay tres implementaciones de IFileProvider disponibles.

Implementación Descripción
PhysicalFileProvider El proveedor físico se utiliza para acceder a los archivos físicos del sistema.
ManifestEmbeddedFileProvider El proveedor insertado de manifiestos se utiliza para tener acceder a archivos insertados en ensamblados.
CompositeFileProvider El proveedor compuesto se utiliza para proporcionar acceso combinado a archivos y directorios de uno o más proveedores.

PhysicalFileProvider

PhysicalFileProvider proporciona acceso al sistema de archivos físico. PhysicalFileProvider usa el tipo System.IO.File (para el proveedor físico) y define el ámbito de todas las rutas de acceso a un directorio y sus elementos secundarios. Esta definición de ámbito impide el acceso al sistema de archivos fuera del directorio especificado y sus elementos secundarios. El escenario más común para crear y usar PhysicalFileProvider es solicitar IFileProvider en un constructor mediante la inyección de dependencias.

Al crear una instancia de este proveedor directamente, se requiere una ruta de acceso del directorio, que actúa como la ruta de acceso base para todas las solicitudes realizadas usando el proveedor.

El código siguiente muestra cómo crear PhysicalFileProvider y usarlo para obtener el contenido del directorio y la información del archivo:

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

Tipos del ejemplo anterior:

  • provider es IFileProvider.
  • contents es IDirectoryContents.
  • fileInfo es IFileInfo.

El proveedor de archivos puede usarse para iterar por el directorio especificado por applicationRoot o llamar a GetFileInfo para obtener información de un archivo. El proveedor de archivos no tiene acceso fuera del directorio applicationRoot.

La aplicación de ejemplo crea el proveedor en la clase Startup.ConfigureServices de la aplicación mediante IHostingEnvironment.ContentRootFileProvider:

var physicalProvider = _env.ContentRootFileProvider;

ManifestEmbeddedFileProvider

ManifestEmbeddedFileProvider se utiliza para acceder a archivos insertados dentro de ensamblados. ManifestEmbeddedFileProvider utiliza un manifiesto compilado en el ensamblado para reconstruir las rutas de acceso originales de los archivos incrustados.

Para generar un manifiesto de los archivos incrustados, establezca la propiedad <GenerateEmbeddedFilesManifest> en true. Especifique los archivos que desea incrustar con <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 patrones globales para especificar uno o varios archivos para incrustar en el ensamblado.

La aplicación de ejemplo crea ManifestEmbeddedFileProvider y pasa el ensamblado que se está ejecutando actualmente a su constructor.

Startup.cs:

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

Las sobrecargas adicionales le permiten:

  • Especificar una ruta de acceso de archivo relativa.
  • Definir el ámbito de archivos a la fecha de la última modificación.
  • Asignar nombre al recurso incrustado que contiene el manifiesto del archivo incrustado.
Sobrecarga Descripción
ManifestEmbeddedFileProvider(Assembly, String) Acepta parámetro de ruta de acceso relativa root opcional. Especifique root para definir el ámbito de las llamadas en GetDirectoryContents en aquellos recursos que se encuentran bajo las rutas de acceso proporcionadas.
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) Acepta un parámetro de ruta de acceso relativa root opcional y un parámetro de fecha lastModified (DateTimeOffset). La fecha lastModified define el ámbito de la última fecha de modificación para las instancias de IFileInfo que IFileProvider devuelve.
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) Acepta una ruta de acceso relativa root opcional, una fecha lastModified y parámetros manifestName. manifestName representa el nombre del recurso incrustado que contiene el manifiesto.

CompositeFileProvider

CompositeFileProvider combina instancias de IFileProvider, y expone una única interfaz para trabajar con archivos de varios proveedores. Al crear CompositeFileProvider, se pasan una o varias instancias de IFileProvider a su constructor.

En la aplicación de ejemplo, PhysicalFileProvider y ManifestEmbeddedFileProvider proporcionan archivos a un CompositeFileProvider registrado en el contenedor de servicios de la aplicación:

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

services.AddSingleton<IFileProvider>(compositeProvider);

Observación de cambios

El método IFileProvider.Watch proporciona un escenario para ver uno o más archivos o directorios para detectar cambios. Watch acepta una cadena de ruta de acceso, que puede usar patrones globales para especificar varios archivos. Watch devuelve un valor de IChangeToken. El token de cambio expone:

  • HasChanged: una propiedad que se puede inspeccionar para determinar si se ha producido un cambio.
  • RegisterChangeCallback: se llama cuando se detectan cambios en la cadena de ruta de acceso especificada. Cada token de cambio solo llama a su devolución de llamada asociada en respuesta a un único cambio. Para habilitar la supervisión constante, puede usar TaskCompletionSource<TResult> (como se muestra a continuación) o volver a crear instancias de IChangeToken en respuesta a los cambios.

En la aplicación de ejemplo, la aplicación de consola WatchConsole se configura para mostrar un mensaje cada vez que se modifica un archivo de texto:

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

Algunos sistemas de archivos, como contenedores de Docker y recursos compartidos de red, no pueden enviar notificaciones de cambio de forma confiable. Establezca la variable de entorno DOTNET_USE_POLLING_FILE_WATCHER en 1 o true para sondear el sistema de archivos en busca de cambios cada cuatro segundos (no configurable).

Patrones globales

Las rutas de acceso del sistema de archivos utilizan patrones de caracteres comodín denominados patrones globales. Especifique grupos de archivos con estos patrones. Los dos caracteres comodín son * y **:

*
Coincide con cualquier elemento del nivel de carpeta actual, con cualquier nombre de archivo o con cualquier extensión de archivo. Las coincidencias finalizan con los caracteres / y . en la ruta de acceso de archivo.

**
Coincide con cualquier elemento de varios niveles de directorios. Puede usarse para coincidir de forma recursiva con muchos archivos dentro de una jerarquía de directorios.

Ejemplos de patrones globales

directory/file.txt
Coincide con un archivo concreto en un directorio específico.

directory/*.txt
Coincide con todos los archivos que tengan la extensión .txt en un directorio específico.

directory/*/appsettings.json
Coincide con todos los archivos appsettings.json que estén en directorios exactamente un nivel por debajo de la carpeta directorio.

directory/**/*.txt
Coincide con todos los archivos que tengan la extensión .txt y se encuentren en cualquier lugar de la carpeta directorio.