Поделиться через


Поставщики файлов в ASP.NET Core

Автор: Стив Смит (Steve Smith)

ASP.NET Core абстрагирует доступ к файловой системе с помощью поставщиков файлов. В рамках платформы ASP.NET Core используются поставщики файлов. Например:

Просмотреть или скачать образец кода (описание загрузки)

Интерфейсы поставщика файлов

Основной интерфейс — IFileProvider. IFileProvider предоставляет методы для следующих действий:

  • получение сведений о файле (IFileInfo);
  • получение сведений о каталоге (IDirectoryContents);
  • настройка уведомлений об изменениях (с помощью IChangeToken).

IFileInfo предоставляет методы и свойства для работы с файлами:

Считывать данные из файла можно с помощью метода IFileInfo.CreateReadStream.

Пример приложения FileProviderSample демонстрирует, как настроить поставщик файлов в Startup.ConfigureServices, чтобы использовать его в приложении путем внедрения зависимостей.

Реализации поставщиков файлов

В следующей таблице перечислены реализации IFileProvider.

Внедрение Description
Составной поставщик файлов Используется для предоставления комбинированного доступа к файлам и каталогам из одного или нескольких поставщиков.
Поставщик внедренных файлов манифестов Используется для доступа к файлам, внедренным в сборки.
Поставщик физических файлов Используется для доступа к физическим файлам системы.

Поставщик физических файлов

PhysicalFileProvider предоставляет доступ к физической файловой системе. PhysicalFileProvider использует тип System.IO.File (для физического поставщика), ограничивая все пути каталогом и его дочерними элементами. Такая привязка к области защищает от доступа к файловой системе за пределами указанного каталога и его дочерних элементов. Самый распространенный подход к созданию и использованию PhysicalFileProvider — запросить IFileProvider в конструкторе путем внедрения зависимостей.

При непосредственном создании экземпляра этого поставщика нужно указать абсолютный путь к каталогу, который станет базовым путем для всех запросов, выполненных с помощью этого поставщика. Стандартные маски не поддерживаются в пути к каталогу.

Следующий код демонстрирует, как использовать PhysicalFileProvider для получения содержимого каталога и сведений о файлах:

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

В предшествующем примере используются следующие типы:

  • Аргумент provider имеет тип IFileProvider.
  • Аргумент contents имеет тип IDirectoryContents.
  • Аргумент fileInfo имеет тип IFileInfo.

С помощью поставщика файлов вы можете выполнить итерацию по каталогу, указанному в параметре applicationRoot, или вызвать GetFileInfo для получения сведений о файлах. Стандартные маски невозможно передать методу GetFileInfo. Поставщик файлов не имеет доступ к каталогам за пределами applicationRoot.

Пример FileProviderSample приложения создает поставщика в методе Startup.ConfigureServices с помощью IHostEnvironment.ContentRootFileProvider:

var physicalProvider = _env.ContentRootFileProvider;

Поставщик внедренных файлов манифестов

ManifestEmbeddedFileProvider используется для доступа к файлам, внедренным в сборки. ManifestEmbeddedFileProvider использует манифест, скомпилированный в сборку, для воссоздания исходных путей для внедренных файлов.

Чтобы создать манифест для внедренных файлов, сделайте следующее:

  1. Добавьте в проект пакет NuGet Microsoft.Extensions.FileProviders.Embedded.

  2. Установите свойство <GenerateEmbeddedFilesManifest> в значение true. Укажите файлы для внедрения с <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>
    

Используйте стандартные маски для указания одного или нескольких файлов, которые вы хотите внедрить в сборку.

Наш пример приложения FileProviderSample создает ManifestEmbeddedFileProvider и передает текущую выполняемую сборку в соответствующий конструктор.

Startup.cs:

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

Дополнительные перегрузки позволяют сделать следующее:

  • указать относительный путь к файлу;
  • ограничить файлы по дате последнего изменения;
  • указать имя внедренного ресурса, содержащего внедренный файл манифеста.
Перегрузка Description
ManifestEmbeddedFileProvider(Assembly, String) Принимает необязательный параметр root со значением относительного пути. Укажите root, чтобы ограничить вызовы к GetDirectoryContents только ресурсами по указанному пути.
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) Принимает необязательный параметр root со значением относительного пути и параметр lastModified со значением даты (DateTimeOffset). Параметр lastModified ограничивает дату последнего изменения для экземпляров IFileInfo, возвращаемых функцией IFileProvider.
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) Принимает необязательный параметр root со значением относительного пути, дату lastModified и параметры manifestName. manifestName здесь представляет имя встроенного ресурса, содержащего манифест.

Составной поставщик файлов

CompositeFileProvider объединяет экземпляры IFileProvider, предоставляя единый интерфейс для работы с файлами от нескольких поставщиков. При создании CompositeFileProvider передайте в соответствующий конструктор один или несколько экземпляров IFileProvider:

В нашем примере приложения FileProviderSample поставщики PhysicalFileProvider и ManifestEmbeddedFileProvider предоставляют файлы для CompositeFileProvider с регистрацией в контейнере служб приложения. Следующий код находится в методе Startup.ConfigureServices проекта:

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

services.AddSingleton<IFileProvider>(compositeProvider);

Отслеживание изменений

Метод IFileProvider.Watch позволяет просматривать один или несколько файлов или каталогов на предмет изменений. Метод Watch:

  • Принимает строку пути, которая может использовать стандартные маски для указания нескольких файлов.
  • Возвращает значение типа IChangeToken.

Итоговый токен изменений предоставляет следующие свойства:

  • HasChanged: свойство, которое можно проверить, чтобы определить, произошло ли изменение.
  • RegisterChangeCallback: вызывается при обнаружении изменений в указанной строке пути. Каждый токен изменения выполняет соответствующий обратный вызов только в ответ на отдельное изменение. Чтобы реализовать непрерывный мониторинг, используйте TaskCompletionSource<TResult>, как показано ниже, или повторно создавайте экземпляры IChangeToken в ответ на изменения.

Пример WatchConsole приложения записывает сообщение при изменении .txt файла в каталоге 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");
}

Некоторые файловые системы, такие как контейнеры Docker и сетевые папки, не могут надежно отправлять уведомления об изменениях. Задайте для переменной среды DOTNET_USE_POLLING_FILE_WATCHER значение 1 или true, чтобы опрашивать файловую систему на предмет изменений каждые 4 секунды (это значение нельзя изменить).

Стандартные маски

В путях файловой системы используются шаблоны подстановочных знаков, которые называются стандартными масками. Эти маски позволяют указывать группы файлов. Поддерживаются два подстановочных знака — * и **.

*
Совпадает с любым элементом на текущем уровне папок, любым именем или расширением файла. Совпадения завершаются символами / и . в пути файла.

**
Совпадает со всем содержимым на разных уровнях каталогов. Может использоваться для рекурсивного сопоставления множества файлов в иерархии каталогов.

Следующая таблица содержит типичные примеры стандартных масок.

Шаблон Description
directory/file.txt Соответствует конкретному файлу в заданном каталоге.
directory/*.txt Соответствует всем файлам с расширением .txt в заданном каталоге.
directory/*/appsettings.json Соответствует всем appsettings.json файлам в каталогах ровно один уровень ниже directory папки.
directory/**/*.txt Соответствует всем файлам с расширением, найденным .txt в любом месте в папке directory .

ASP.NET Core абстрагирует доступ к файловой системе с помощью поставщиков файлов. В рамках платформы ASP.NET Core используются поставщики файлов.

Просмотреть или скачать образец кода (описание загрузки)

Интерфейсы поставщика файлов

Основной интерфейс — IFileProvider. IFileProvider предоставляет методы для следующих действий:

  • получение сведений о файле (IFileInfo);
  • получение сведений о каталоге (IDirectoryContents);
  • настройка уведомлений об изменениях (с помощью IChangeToken).

IFileInfo предоставляет методы и свойства для работы с файлами:

Данные из файла можно считывать с помощью метода IFileInfo.CreateReadStream.

Пример приложения демонстрирует, как настроить в Startup.ConfigureServices поставщике файлов, чтобы использовать его в приложении путем внедрения зависимостей.

Реализации поставщиков файлов

Доступны три реализации IFileProvider.

Внедрение Description
PhysicalFileProvider Физический поставщик используется для доступа к физическим файлам системы.
ManifestEmbeddedFileProvider Поставщик внедренных манифестов используется для доступа к файлам, внедренным в сборки.
CompositeFileProvider Составной поставщик используется для предоставления комбинированного доступа к файлам и каталогам из одного или нескольких поставщиков.

PhysicalFileProvider

PhysicalFileProvider предоставляет доступ к физической файловой системе. PhysicalFileProvider использует тип System.IO.File (для физического поставщика), ограничивая все пути каталогом и его дочерними элементами. Такая привязка к области защищает от доступа к файловой системе за пределами указанного каталога и его дочерних элементов. Самый распространенный подход к созданию и использованию PhysicalFileProvider — запросить IFileProvider в конструкторе путем внедрения зависимостей.

При непосредственном создании экземпляра этого поставщика нужно указать путь к каталогу, который станет базовым путем для всех запросов, выполненных с помощью этого поставщика.

Следующий пример кода демонстрирует, как создать и использовать PhysicalFileProvider для получения содержимого каталога и сведений о файлах:

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

В предшествующем примере используются следующие типы:

  • Аргумент provider имеет тип IFileProvider.
  • Аргумент contents имеет тип IDirectoryContents.
  • Аргумент fileInfo имеет тип IFileInfo.

С помощью поставщика файлов вы можете выполнить итерацию по каталогу, указанному в параметре applicationRoot, или вызвать GetFileInfo для получения сведений о файлах. Поставщик файлов не имеет доступ к каталогам за пределами applicationRoot.

Этот пример приложения создает поставщик для приложения в классе Startup.ConfigureServices, используя IHostingEnvironment.ContentRootFileProvider:

var physicalProvider = _env.ContentRootFileProvider;

ManifestEmbeddedFileProvider

ManifestEmbeddedFileProvider используется для доступа к файлам, внедренным в сборки. ManifestEmbeddedFileProvider использует манифест, скомпилированный в сборку, для воссоздания исходных путей для внедренных файлов.

Чтобы создать манифест для внедренных файлов, задайте для свойства <GenerateEmbeddedFilesManifest> значение true. Выберите файлы для внедрения с помощью <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>

Используйте стандартные маски для указания одного или нескольких файлов, которые вы хотите внедрить в сборку.

Наш пример приложения создает ManifestEmbeddedFileProvider и передает в соответствующий конструктор текущую выполняемую сборку.

Startup.cs:

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

Дополнительные перегрузки позволяют сделать следующее:

  • указать относительный путь к файлу;
  • ограничить файлы по дате последнего изменения;
  • указать имя внедренного ресурса, содержащего внедренный файл манифеста.
Перегрузка Description
ManifestEmbeddedFileProvider(Assembly, String) Принимает необязательный параметр root со значением относительного пути. Укажите root, чтобы ограничить вызовы к GetDirectoryContents только ресурсами по указанному пути.
ManifestEmbeddedFileProvider(Assembly, String, DateTimeOffset) Принимает необязательный параметр root со значением относительного пути и параметр lastModified со значением даты (DateTimeOffset). Параметр lastModified ограничивает дату последнего изменения для экземпляров IFileInfo, возвращаемых функцией IFileProvider.
ManifestEmbeddedFileProvider(Assembly, String, String, DateTimeOffset) Принимает необязательный параметр root со значением относительного пути, дату lastModified и параметры manifestName. manifestName здесь представляет имя встроенного ресурса, содержащего манифест.

CompositeFileProvider

CompositeFileProvider объединяет экземпляры IFileProvider, предоставляя единый интерфейс для работы с файлами от нескольких поставщиков. При создании CompositeFileProvider передайте в соответствующий конструктор один или несколько экземпляров IFileProvider:

В нашем примере приложения PhysicalFileProvider и ManifestEmbeddedFileProvider предоставляют файлы для CompositeFileProvider с регистрацией в контейнере служб приложения:

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

services.AddSingleton<IFileProvider>(compositeProvider);

Отслеживание изменений

Метод IFileProvider.Watch позволяет контролировать изменения в одном или нескольких файлах или каталогах. Watch принимает строку пути, которая можно использовать стандартные маски для указания нескольких файлов. Watch возвращает IChangeToken. Этот токен изменений предоставляет следующие свойства:

  • HasChanged: свойство, которое можно проверить, чтобы определить, произошло ли изменение.
  • RegisterChangeCallback: вызывается при обнаружении изменений в указанной строке пути. Каждый токен изменения выполняет соответствующий обратный вызов только в ответ на отдельное изменение. Чтобы реализовать непрерывный мониторинг, используйте TaskCompletionSource<TResult>, как показано ниже, или повторно создавайте экземпляры IChangeToken в ответ на изменения.

В нашем примере консольное приложение WatchConsole будет отображать сообщение при изменении текстового файла:

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

Некоторые файловые системы, такие как контейнеры Docker и сетевые папки, не могут надежно отправлять уведомления об изменениях. Задайте для переменной среды DOTNET_USE_POLLING_FILE_WATCHER значение 1 или true, чтобы опрашивать файловую систему на предмет изменений каждые 4 секунды (это значение нельзя изменить).

Стандартные маски

В путях файловой системы используются шаблоны подстановочных знаков, которые называются стандартными масками. Эти маски позволяют указывать группы файлов. Поддерживаются два подстановочных знака — * и **.

*
Совпадает с любым элементом на текущем уровне папок, любым именем или расширением файла. Совпадения завершаются символами / и . в пути файла.

**
Совпадает со всем содержимым на разных уровнях каталогов. Может использоваться для рекурсивного сопоставления множества файлов в иерархии каталогов.

Примеры стандартных масок

directory/file.txt
Соответствует конкретному файлу в заданном каталоге.

directory/*.txt
Соответствует всем файлам с расширением .txt в заданном каталоге.

directory/*/appsettings.json
Соответствует всем файлам appsettings.json в любом каталоге, расположенном ровно на один уровень ниже папки directory.

directory/**/*.txt
Соответствует всем файлам с расширением .txt, находящимся на любом уровне ниже каталога directory.