Поставщик изображений в ASP. NET Core 3.1 - PullRequest
0 голосов
/ 28 января 2020

У меня есть ASP. NET Базовый веб-API, называемый «хранилище», написанный с использованием C#. Это приложение предназначено для обслуживания многих веб-приложений с изображениями. Используя другие имеющиеся у меня приложения, я могу запрашивать изображения из хранилища, используя HTTP-запрос.

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

public class MyCustomImagesProvider : IFileProvider
{
    private readonly IHttpClientFactory ClientFactory;

    public MyCustomImagesProvider(IHttpClientFactory clientFactory)
    {
        ClientFactory = clientFactory;
    }

    public IFileInfo GetFileInfo(string subpath)
    {
        try
        {
            var client = ClientFactory.CreateClient("client-name");

            Uri url = new Uri("https://storage.mydomain.com", $"api/files/get?name={subpath}");

            var result = client.GetAsync(url).Result;

            result.EnsureSuccessStatusCode();
            string filename = Path.GetFileName(subpath);
            DateTime lastModifiedAt = (result.Content.Headers.LastModified?.DateTime) ?? DateTime.Now;
            var stream = result.Content.ReadAsStreamAsync().Result;

            return new MyCustomFileInfo(stream, filename, lastModifiedAt);
        }
        catch
        {
        }

        return new MyCustomFileInfo
        {
            Exists = false
        };
    }

    public IDirectoryContents GetDirectoryContents(string subpath) => throw new NotImplementedException();
    public IChangeToken Watch(string filter) => throw new NotImplementedException();
}

В некоторых из моих приложений требуется только миниатюра изображения, тогда как для других требуется полный размер и / или миниатюра изображения. Позже мне может даже понадобиться изображение среднего размера для некоторых приложений (возможно, 4 разных размера одного и того же изображения). Ограничение дискового пространства становится проблемой, поскольку мне приходится дублировать одно и то же изображение на несколько файлов (по одному для каждого размера) для размещения всех моих приложений.

Другими словами, если у меня есть файл с именем "abc_fullsize.jpg", то мне нужно будет создать и сохранить "abc_thumbnail.jpg" и "abc_midside.jpg", для которых требуется много места на диске.

Вопрос

Было бы целесообразно создавать эти изображения на лету вместо того, чтобы создавать их на столе?

Я думаю, у меня будет ThumbnailImageProvider, MidsizeImageProvider и FullsizeImageProvider, каждый из которых отправит требуемый максимальный размер для приложения хранения, а приложение хранения будет считывать полноразмерное изображение со стола и затем отвечать измененным размером образ. Вот пример ThumbnailImageProvider

public class ThumbnailImageProvider : IFileProvider
{
    private readonly IHttpClientFactory ClientFactory;

    public ThumbnailImageProvider(IHttpClientFactory clientFactory)
    {
        ClientFactory = clientFactory;
    }

    public IFileInfo GetFileInfo(string subpath)
    {
        try
        {
            var client = ClientFactory.CreateClient("client-name");
            // Note the max-width=250 parameter here would tell the API that I need this image recreated on the fly with max width of 250.
            Uri url = new Uri("https://storage.mydomain.com", $"api/files/get?name={subpath}&maxWidth=250");

            var result = client.GetAsync(url).Result;

            result.EnsureSuccessStatusCode();
            string filename = Path.GetFileName(subpath);
            DateTime lastModifiedAt = (result.Content.Headers.LastModified?.DateTime) ?? DateTime.Now;
            var stream = result.Content.ReadAsStreamAsync().Result;

            return new MyCustomFileInfo(stream, filename, lastModifiedAt);
        }
        catch
        {
        }

        return new MyCustomFileInfo
        {
            Exists = false
        };
    }

    public IDirectoryContents GetDirectoryContents(string subpath) => throw new NotImplementedException();
    public IChangeToken Watch(string filter) => throw new NotImplementedException();
}

Метод действия на конечной точке api/files/get ответит следующим действием

[HttpGet("get")]
public IActionResult Get(string name, int? maxWidth)
{
    IFileInfo fileInfo = FileProvider.GetFileInfo(name);

    if (!fileInfo.Exists)
    {
        return NotFound();
    }

    if(maxWidth.HasValue && maxWidth > 1)
    {
        Image image = ImageProcessor.GetResizedImage(fileInfo.CreateReadStream(), maxWidth.Value, out ImageFormat imageFormat);

        using var memoryStream = new MemoryStream();
        image.Save(memoryStream, imageFormat);

        string extension = Path.GetExtension(fileInfo.Name);
        string rawFilename = Path.GetFileNameWithoutExtension(fileInfo.Name);

        string filename = $"{rawFilename}-{maxWidth}{extension}";

        return File(memoryStream, "image/jpeg", filename, fileInfo.LastModified, null);
    }

    return File(fileInfo.CreateReadStream(), "image/jpeg", fileInfo.Name, fileInfo.LastModified, null);
}

1 Ответ

0 голосов
/ 29 января 2020

Я думаю, что вам лучше применить логи c к промежуточному программному обеспечению. Посмотрите, как работает промежуточное программное обеспечение для файлов pnet stati c:

https://github.com/dotnet/aspnetcore/blob/19d2f6124f5d04859e350d1f5a01e994e14ef1ce/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs

У вашего подхода есть две проблемы:

  1. IFileProvider не поддерживает asyn c. Вы совершаете сетевые вызовы, которые являются главным кандидатом на роль asyn c
  2. Изменение размеров изображений стоит довольно много ресурсов процессора. Большому сайту с примерно 300 000 статей (и примерно таким же количеством изображений) потребовалось несколько дней высокой производительности процессора с двумя 4-ядерными веб-серверами, прежде чем кешировать все на диск.

Поэтому моя рекомендация кэшировать изображения на диске (или в хранилище больших двоичных объектов при использовании кэша вывода) -> https://docs.microsoft.com/en-us/aspnet/core/performance/caching/middleware?view=aspnetcore-3.1

...