繁体   English   中英

ASP.NET Core 3.1 中的图像提供程序

[英]Image provider in ASP.NET Core 3.1

我有一个基于 ASP.NET Core 的名为“存储”的 Web 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”,这需要大量磁盘空间。

动态创建这些图像而不是在桌面上创建它们是否有效?

我想我将有一个ThumbnailImageProviderMidsizeImageProviderFullsizeImageProvider每个就送需要最大尺寸为存储应用程序和存储的应用程序将读取来自台全尺寸的图像,然后用调整后的图像响应。 这是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);
}

我认为您最好将您的逻辑应用于中间件。 看看aspnet的静态文件中间件是如何工作的:

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

你的方法有两个问题:

  1. IFileProvider 不支持异步。 您正在进行网络调用,这是异步的主要候选者
  2. 调整图像大小会消耗相当多的 CPU。 一个拥有大约 300.000 篇文章(和大约相同数量的图片)的大型站点在将所有内容缓存到磁盘之前需要几天的高 CPU 和两个 4 核 Web 服务器。

所以我的建议是在磁盘上缓存图像(或使用输出缓存时的 blob 存储)-> https://docs.microsoft.com/en-us/aspnet/core/performance/caching/middleware?view=aspnetcore- 3.1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM