You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ImageSharp自定义ImageProvider的Azure缓存失效问题排查

问题:ImageSharp Azure缓存未生效,后续请求仍走自定义Provider

我基于.NET 6和ImageSharp做POC,要实现带Azure缓存的自定义ImageProvider。图片实际存储在SQL数据库,目前用静态文件模拟加载逻辑,需求是根据URL中的GUID从数据库加载图片并缓存到Azure Blob。

未找到该场景的教程,已在Program.cs中添加相关配置:

builder.Services.AddImageSharp()
        .Configure<AzureBlobStorageImageProviderOptions>(options =>
        {
            // The "BlobContainers" collection allows registration of multiple containers.
            options.BlobContainers.Add(new AzureBlobContainerClientOptions
            {
                ConnectionString = appSettings["string"],
                ContainerName = "cache"
            });
        })
        .ClearProviders()
        .AddProvider<AzureBlobStorageImageProvider>()
        .Configure<AzureBlobStorageCacheOptions>(options =>
        {
            options.ConnectionString = appSettings["string"];
            options.ContainerName = "cache";

            // Optionally create the cache container on startup if not already created.
            AzureBlobStorageCache.CreateIfNotExists(options, PublicAccessType.None);
        }).AddProvider<LoadImageFromDatabaseProvider>().SetCache<AzureBlobStorageCache>()
        .AddProvider<PhysicalFileSystemProvider>()
        .SetCache<AzureBlobStorageCache>();

自定义实现代码(POC版本):

namespace ImageService
{
    public class LoadImageFromDatabaseProvider : IImageProvider
    {
        private readonly ImageRepository _imageRepository;

        /// <summary>
        /// A match function used by the resolver to identify itself as the correct resolver to use.
        /// </summary>
        private Func<HttpContext, bool> _match;

        public LoadImageFromDatabaseProvider(ImageRepository repository)
        {
            _imageRepository = repository;
        }

        public ProcessingBehavior ProcessingBehavior { get; } = ProcessingBehavior.All;

        public Func<HttpContext, bool> Match
        {
            get => _match ?? IsMatch;
            set => _match = value;
        }

        public async Task<IImageResolver> GetAsync(HttpContext context)
        {
            //Extract image name from the querystring e.g. /image?id=<imagei>

            string filename = Path.GetFileName(context.Request.Path);

            var id = filename.Split(".").FirstOrDefault();

            if (!string.IsNullOrEmpty(id))
            {
                var (image, metadata) = await _imageRepository.Get(id);

                return new ImageResolver(image, metadata);
            }
            return null;
        }

        public bool IsValidRequest(HttpContext context) => true;

        private bool IsMatch(HttpContext context)
        {
            return context.Request.Path.Value.Contains("productimages");
        }
    }

    public class ImageResolver : IImageResolver
    {
        private byte[] _data;
        private Metadata _metadata;

        public ImageResolver(byte[] data, Metadata metadata)
        {
            _data = data;
            _metadata = metadata;
        }

        public Task<Stream> OpenReadAsync()
        {
            return Task.FromResult<Stream>(new MemoryStream(_data));
        }

        Task<SixLabors.ImageSharp.Web.ImageMetadata> IImageResolver.GetMetaDataAsync()
        {
            return Task.FromResult(new SixLabors.ImageSharp.Web.ImageMetadata(_metadata.CreatedOn, _data.Length));
        }
    }

    public class Metadata
    {
        public DateTime CreatedOn { get; internal set; }
    }

    public class ImageRepository

    {
        public ImageRepository()
        {
        }

        public async Task<(byte[] image, Metadata metadata)> Get(string id)
        {
            string path = Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot\imgs\estonian-unicorn-in-space.jpg");

            byte[] bytes = System.IO.File.ReadAllBytes(path);

            return (bytes, new Metadata() { CreatedOn = DateTime.Now });
        }
    }
}

当前问题:首次加载图片时已将其缓存到Azure Blob,但后续请求仍从自定义ImageProvider获取图片,且Azure中缓存图片的时间戳会更新,请问缓存未生效缺少哪些配置?


解决方案

1. 调整Provider注册顺序(核心)

ImageSharp会按Provider注册顺序匹配请求,必须把AzureBlobStorageImageProvider放在最前面,让请求优先检查Azure缓存是否存在,只有缓存不存在时才会走自定义Provider加载源图片。

2. 移除重复的SetCache调用

你连续两次调用SetCache<AzureBlobStorageCache>(),重复调用会导致逻辑冲突,只需要在配置完缓存后调用一次即可。

3. 修正缓存键的生成逻辑(关键)

当前ImageRepository每次返回的Metadata.CreatedOn都是DateTime.Now,这会导致每次请求的图片元数据创建时间都不一样,ImageSharp生成的缓存键也会随之变化,所以每次都会重新生成缓存。需要把CreatedOn改成图片的固定创建时间(比如从数据库读取的实际值,或者本地文件的创建时间)。

4. 优化IsValidRequest逻辑

当前自定义Provider的IsValidRequest直接返回true,会匹配所有请求,即使缓存已经存在。可以结合Match逻辑,让它只匹配包含productimages的路径,避免和缓存Provider的匹配范围重叠。


修改后的代码示例

调整后的Program.cs配置

builder.Services.AddImageSharp()
    // 先配置并设置缓存
    .Configure<AzureBlobStorageCacheOptions>(options =>
    {
        options.ConnectionString = appSettings["string"];
        options.ContainerName = "cache";
        AzureBlobStorageCache.CreateIfNotExists(options, PublicAccessType.None);
    })
    .SetCache<AzureBlobStorageCache>()
    // 优先注册缓存Provider
    .AddProvider<AzureBlobStorageImageProvider>()
    .Configure<AzureBlobStorageImageProviderOptions>(options =>
    {
        options.BlobContainers.Add(new AzureBlobContainerClientOptions
        {
            ConnectionString = appSettings["string"],
            ContainerName = "cache"
        });
    })
    // 再注册自定义Provider和物理文件Provider
    .AddProvider<LoadImageFromDatabaseProvider>()
    .AddProvider<PhysicalFileSystemProvider>();

修改后的ImageRepository

public async Task<(byte[] image, Metadata metadata)> Get(string id)
{
    string path = Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot\imgs\estonian-unicorn-in-space.jpg");
    byte[] bytes = System.IO.File.ReadAllBytes(path);
    
    // 用文件的实际创建时间代替当前时间,或者从数据库读取图片的创建时间
    var fileInfo = new FileInfo(path);
    return (bytes, new Metadata() { CreatedOn = fileInfo.CreationTime });
}

内容的提问来源于stack exchange,提问作者Bjarke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 11:57:17