Fournisseurs de fichiers dans ASP.NET Core

Par Steve Smith

ASP.NET Core fournit un accès au système de fichiers en utilisant des fournisseurs de fichiers. Des fournisseurs de fichiers sont utilisés dans l’infrastructure ASP.NET Core. Par exemple :

  • IWebHostEnvironment expose la racine web et la racine de contenu de l’application sous la forme de types IFileProvider.
  • L’intergiciel (middleware) de fichiers statiques utilise des fournisseurs de fichiers pour localiser les fichiers statiques.
  • Razor utilise des fournisseurs de fichiers pour localiser des pages et des vues.
  • Les outils .NET Core utilisent des fournisseurs de fichiers et des modèles d’utilisation des caractères génériques pour spécifier les fichiers qui doivent être publiés.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Interfaces de fournisseur de fichiers

L’interface principale est IFileProvider. IFileProvider expose des méthodes pour :

  • Obtenir des informations sur le fichier (IFileInfo).
  • Obtenir des informations sur le répertoire (IDirectoryContents).
  • Configurer des notifications de modification (à l’aide un IChangeToken).

IFileInfo fournit des méthodes et propriétés d’utilisation des fichiers :

Vous pouvez lire dans le fichier en utilisant la méthode IFileInfo.CreateReadStream.

L’exemple d’application FileProviderSample montre comment configurer un fournisseur de fichiers dans Startup.ConfigureServices pour une utilisation dans toute l’application via l’injection de dépendances.

Implémentations de fournisseur de fichiers

Le tableau suivant répertorie les implémentations de IFileProvider.

Implémentation Description
Fournisseur de fichiers composites Utilisé pour fournir un accès combiné à des fichiers et à des répertoires à partir d’un ou de plusieurs fournisseurs.
Fournisseur de fichiers incorporés de manifeste Utilisé pour accéder à des fichiers incorporés dans des assemblys.
Fournisseur de fichiers physiques Utilisé pour accéder aux fichiers physiques du système.

Fournisseur de fichiers physiques

La classe PhysicalFileProvider fournit un accès au système de fichiers physique. PhysicalFileProvider utilise le type System.IO.File (pour le fournisseur physique) et définissant comme portée tous les chemins d’un répertoire et de ses enfants. Cette portée empêche l’accès au système de fichiers en dehors du répertoire spécifié et ses enfants. Le scénario le plus courant pour créer et utiliser un PhysicalFileProvider consiste à demander un IFileProvider dans un constructeur par le biais de l’injection de dépendances.

Lors de l’instanciation directe de ce fournisseur, un chemin de répertoire absolu est obligatoire et sert de chemin de base pour toutes les demandes effectuées à l’aide du fournisseur. Les modèles glob ne sont pas pris en charge dans le chemin d’accès du répertoire.

Le code suivant montre comment utiliser un PhysicalFileProvider pour obtenir le contenu du répertoire et les informations sur le fichier :

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

Types dans l’exemple précédent :

  • provider est une IFileProvider.
  • contents est une IDirectoryContents.
  • fileInfo est une IFileInfo.

Le fournisseur de fichiers peut être utilisé pour itérer au sein du répertoire spécifié par applicationRoot ou pour appeler GetFileInfo afin d’obtenir des informations sur un fichier. Les modèles glob ne peuvent pas être passés à la méthode GetFileInfo. Le fournisseur de fichiers n’a pas accès en dehors du répertoire applicationRoot.

L’exemple d’application FileProviderSample crée le fournisseur dans la méthode Startup.ConfigureServices à l’aide de IHostEnvironment.ContentRootFileProvider :

var physicalProvider = _env.ContentRootFileProvider;

Fournisseur de fichiers incorporés de manifeste

ManifestEmbeddedFileProvider est utilisé pour accéder à des fichiers incorporés dans des assemblys. ManifestEmbeddedFileProvider utilise un manifeste compilé dans l’assembly pour reconstruire les chemins d’accès d’origine des fichiers intégrés.

Pour générer un manifeste des fichiers incorporés :

  1. Ajoutez le package NuGet Microsoft.Extensions.FileProviders.Embedded à votre projet.

  2. Définissez la propriété <GenerateEmbeddedFilesManifest> sur true. Spécifiez les fichiers à incorporer avec <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>
    

Utilisez les modèles glob pour spécifier un ou plusieurs fichiers à incorporer dans l’assembly.

L’exemple d’application FileProviderSample crée un ManifestEmbeddedFileProvider et transmet l’assembly en cours d’exécution à son constructeur.

Startup.cs:

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

Des surcharges supplémentaires vous permettent de :

  • Spécifier un chemin de fichier relatif.
  • Définir la portée de fichiers sur une date de dernière modification.
  • Nommer la ressource incorporée contenant le manifeste de fichier incorporé.
Surcharge Description
ManifestEmbeddedFileProvider(Assembly, String) Accepte un paramètre de chemin d'accès relatif root facultatif. Spécifiez root pour définir la portée des appels à GetDirectoryContents sur ces ressources sous le chemin d’accès fourni.
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) Accepte un paramètre de chemin relatif root facultatif et un paramètre de date lastModified (DateTimeOffset). La date lastModified définit la portée de la dernière date de modification pour les instances IFileInfo retournées par IFileProvider.
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) Accepte un chemin d'accès relatif root facultatif, une date lastModified et des paramètres manifestName. manifestName représente le nom de la ressource incorporée contenant la manifeste.

Fournisseur de fichiers composites

CompositeFileProvider combine des instances IFileProvider, en exposant une interface unique qui permet d’utiliser des fichiers de différents fournisseurs. Quand vous créez CompositeFileProvider, vous passez une ou plusieurs instances IFileProvider à son constructeur.

Dans l’exemple d’application FileProviderSample, un PhysicalFileProvider et un ManifestEmbeddedFileProvider fournissent des fichiers à un CompositeFileProvider inscrit dans le conteneur de service de l’application. Le code suivant se trouve dans la méthode Startup.ConfigureServices du projet :

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

services.AddSingleton<IFileProvider>(compositeProvider);

Suivre les modifications apportées

La méthode IFileProvider.Watch offre un scénario pour observer un ou plusieurs fichiers ou répertoires afin de détecter les changements. La méthode Watch :

  • Accepte une chaîne de chemin d’accès de fichier, qui peut utiliser des modèles Glob pour spécifier plusieurs fichiers.
  • Retourne un IChangeToken.

Le jeton de modification en résultant expose :

  • HasChanged : une propriété qui peut être inspectée pour déterminer si une modification a eu lieu.
  • RegisterChangeCallback : appelée quand des modifications sont détectées dans la chaîne de chemin spécifiée. Chaque jeton de modification appelle uniquement son rappel associé en réponse à un changement unique. Pour activer une surveillance constante, vous pouvez utiliser une TaskCompletionSource<TResult> comme indiqué ci-dessous, ou recréer des instances IChangeToken en réponse aux changements.

L’exemple d’application WatchConsole écrit un message chaque fois qu’un fichier .txt du répertoire TextFiles est modifié :

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

Certains systèmes de fichiers, comme les conteneurs Docker et les partages réseau, peuvent ne pas envoyer de manière fiable les notifications de modifications. Définissez la variable d’environnement DOTNET_USE_POLLING_FILE_WATCHER sur 1 ou true pour interroger le système de fichiers à la recherche de changements toutes les quatre secondes (non configurable).

Modèles Glob

Les chemins de système de fichiers utilisent des modèles à caractères génériques appelés modèles Glob (ou d’utilisation des caractères génériques). Spécifiez les groupes de fichiers avec ces modèles. Les deux caractères génériques sont * et ** :

*
Établit une correspondance avec n’importe quel élément au niveau de dossier actuel, avec n’importe quel nom de fichier ou avec n’importe quelle extension de fichier. Les correspondances sont terminées par des caractères / et . dans le chemin des fichiers.

**
Établit une correspondance avec n’importe quel élément sur plusieurs niveaux de répertoire. Peut être utilisé pour établir une correspondance avec plusieurs fichiers dans une hiérarchie de répertoires de manière récursive.

Le tableau suivant fournit des exemples courants de modèles Glob.

Modèle Description
directory/file.txt Établit une correspondance avec un fichier spécifique dans un répertoire spécifique.
directory/*.txt Établit une correspondance avec tous les fichiers ayant l’extension .txt dans un répertoire spécifique.
directory/*/appsettings.json Établit une correspondance avec tous les fichiers appsettings.json dans les répertoires situés exactement un niveau en dessous du dossier directory.
directory/**/*.txt Établit une correspondance avec tous les fichiers ayant une extension .txt et se trouvant n’importe où sous le dossier directory.

ASP.NET Core fournit un accès au système de fichiers en utilisant des fournisseurs de fichiers. Des fournisseurs de fichiers sont utilisés dans l’infrastructure ASP.NET Core :

  • IHostingEnvironment expose la racine web et la racine de contenu de l’application sous la forme de types IFileProvider.
  • L’intergiciel (middleware) de fichiers statiques utilise des fournisseurs de fichiers pour localiser les fichiers statiques.
  • Razor utilise des fournisseurs de fichiers pour localiser des pages et des vues.
  • Les outils .NET Core utilisent des fournisseurs de fichiers et des modèles d’utilisation des caractères génériques pour spécifier les fichiers qui doivent être publiés.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Interfaces de fournisseur de fichiers

L’interface principale est IFileProvider. IFileProvider expose des méthodes pour :

  • Obtenir des informations sur le fichier (IFileInfo).
  • Obtenir des informations sur le répertoire (IDirectoryContents).
  • Configurer des notifications de modification (à l’aide un IChangeToken).

IFileInfo fournit des méthodes et propriétés d’utilisation des fichiers :

Vous pouvez lire à partir du fichier en utilisant la méthode IFileInfo.CreateReadStream.

L’exemple d’application montre comment configurer un fournisseur de fichiers dans Startup.ConfigureServices pour une utilisation dans toute l’application via l’injection de dépendances.

Implémentations de fournisseur de fichiers

Trois implémentations de IFileProvider sont disponibles.

Implémentation Description
PhysicalFileProvider Le fournisseur physique est utilisé pour accéder aux fichiers physiques du système.
ManifestEmbeddedFileProvider Le fournisseur incorporé de manifeste est utilisé pour accéder à des fichiers incorporés dans des assemblys.
CompositeFileProvider Le fournisseur composite est utilisé pour fournir un accès combiné à des fichiers et à des répertoires à partir d’un ou de plusieurs fournisseurs.

PhysicalFileProvider

La classe PhysicalFileProvider fournit un accès au système de fichiers physique. PhysicalFileProvider utilise le type System.IO.File (pour le fournisseur physique) et définissant comme portée tous les chemins d’un répertoire et de ses enfants. Cette portée empêche l’accès au système de fichiers en dehors du répertoire spécifié et ses enfants. Le scénario le plus courant pour créer et utiliser un PhysicalFileProvider consiste à demander un IFileProvider dans un constructeur par le biais de l’injection de dépendances.

Lors de l’instanciation directe de ce fournisseur, un chemin de répertoire est obligatoire et sert de chemin de base pour toutes les demandes effectuées à l’aide du fournisseur.

Le code suivant montre comment créer un PhysicalFileProvider et l’utiliser pour obtenir le contenu du répertoire et les informations sur le fichier :

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

Types dans l’exemple précédent :

  • provider est une IFileProvider.
  • contents est une IDirectoryContents.
  • fileInfo est une IFileInfo.

Le fournisseur de fichiers peut être utilisé pour itérer au sein du répertoire spécifié par applicationRoot ou pour appeler GetFileInfo afin d’obtenir des informations sur un fichier. Le fournisseur de fichiers n’a pas accès en dehors du répertoire applicationRoot.

L’exemple d’application crée le fournisseur dans la classe Startup.ConfigureServices de l’application à l’aide de IHostingEnvironment.ContentRootFileProvider :

var physicalProvider = _env.ContentRootFileProvider;

ManifestEmbeddedFileProvider

ManifestEmbeddedFileProvider est utilisé pour accéder à des fichiers incorporés dans des assemblys. ManifestEmbeddedFileProvider utilise un manifeste compilé dans l’assembly pour reconstruire les chemins d’accès d’origine des fichiers intégrés.

Pour générer un manifeste des fichiers incorporés, définissez la propriété <GenerateEmbeddedFilesManifest> sur true. Spécifiez les fichiers à incorporer avec <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>

Utilisez les modèles glob pour spécifier un ou plusieurs fichiers à incorporer dans l’assembly.

L’exemple d’application crée un ManifestEmbeddedFileProvider et transmet l’assembly en cours d’exécution à son constructeur.

Startup.cs:

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

Des surcharges supplémentaires vous permettent de :

  • Spécifier un chemin de fichier relatif.
  • Définir la portée de fichiers sur une date de dernière modification.
  • Nommer la ressource incorporée contenant le manifeste de fichier incorporé.
Surcharge Description
ManifestEmbeddedFileProvider(Assembly, String) Accepte un paramètre de chemin d'accès relatif root facultatif. Spécifiez root pour définir la portée des appels à GetDirectoryContents sur ces ressources sous le chemin d’accès fourni.
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) Accepte un paramètre de chemin relatif root facultatif et un paramètre de date lastModified (DateTimeOffset). La date lastModified définit la portée de la dernière date de modification pour les instances IFileInfo retournées par IFileProvider.
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) Accepte un chemin d'accès relatif root facultatif, une date lastModified et des paramètres manifestName. manifestName représente le nom de la ressource incorporée contenant la manifeste.

CompositeFileProvider

CompositeFileProvider combine des instances IFileProvider, en exposant une interface unique qui permet d’utiliser des fichiers de différents fournisseurs. Quand vous créez CompositeFileProvider, vous passez une ou plusieurs instances IFileProvider à son constructeur.

Dans l’exemple d’application, un PhysicalFileProvider et un ManifestEmbeddedFileProvider fournissent des fichiers à un CompositeFileProvider inscrit dans le conteneur de service de l’application :

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

services.AddSingleton<IFileProvider>(compositeProvider);

Suivre les modifications apportées

La méthode IFileProvider.Watch offre un moyen d’observer un ou plusieurs fichiers ou répertoires afin de détecter les changements. Watch accepte une chaîne de chemin, qui peut utiliser des modèles d’utilisation des caractères génériques pour spécifier plusieurs fichiers. WatchRetourne un IChangeToken. Le jeton de modification expose :

  • HasChanged : une propriété qui peut être inspectée pour déterminer si une modification a eu lieu.
  • RegisterChangeCallback : appelée quand des modifications sont détectées dans la chaîne de chemin spécifiée. Chaque jeton de modification appelle uniquement son rappel associé en réponse à un changement unique. Pour activer une surveillance constante, vous pouvez utiliser une TaskCompletionSource<TResult> comme indiqué ci-dessous, ou recréer des instances IChangeToken en réponse aux changements.

Dans l’exemple d’application, l’application console WatchConsole est configurée pour afficher un message chaque fois qu’un fichier texte est modifié :

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

Certains systèmes de fichiers, comme les conteneurs Docker et les partages réseau, peuvent ne pas envoyer de manière fiable les notifications de modifications. Définissez la variable d’environnement DOTNET_USE_POLLING_FILE_WATCHER sur 1 ou true pour interroger le système de fichiers à la recherche de changements toutes les quatre secondes (non configurable).

Modèles Glob

Les chemins de système de fichiers utilisent des modèles à caractères génériques appelés modèles Glob (ou d’utilisation des caractères génériques). Spécifiez les groupes de fichiers avec ces modèles. Les deux caractères génériques sont * et ** :

*
Établit une correspondance avec n’importe quel élément au niveau de dossier actuel, avec n’importe quel nom de fichier ou avec n’importe quelle extension de fichier. Les correspondances sont terminées par des caractères / et . dans le chemin des fichiers.

**
Établit une correspondance avec n’importe quel élément sur plusieurs niveaux de répertoire. Peut être utilisé pour établir une correspondance avec plusieurs fichiers dans une hiérarchie de répertoires de manière récursive.

Exemples de modèles d’utilisation des caractères génériques

directory/file.txt
Établit une correspondance avec un fichier spécifique dans un répertoire spécifique.

directory/*.txt
Établit une correspondance avec tous les fichiers ayant l’extension .txt dans un répertoire spécifique.

directory/*/appsettings.json
Établit une correspondance avec tous les fichiers appsettings.json dans les répertoires situés exactement un niveau en dessous du dossier répertoire.

directory/**/*.txt
Établit une correspondance avec tous les fichiers ayant l’extension .txt et se trouvant n’importe où sous le dossier répertoire.