Search code examples
c#.net.net-coreimagesharp

Imagesharp custom image provider, cache not working


I am trying to make a POC with .net 6 and imagesharp. I am trying to implement a custom imageprovider with cache in azure.

The images are stored ind a SQL database. For now I just load an image from a static file.

The images should be loaded from a database, based on a GUID in the url and the images should be cached in azure.

I have not been able to find any tutorial for this scenario.

I have added the following in the 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>();

My implementation is as follows - keep in mind that it is POC :P

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

I can load an image and the image is cached in azure on the first load. However on the second load the image is still fetch from my provider and not the cache in azure. Also I can see on the timestamp in azure that the cached image is updated.

What am I missing for the cache to work?


Solution

  • You're returning a brand new created date on ever request in ImageRepository.

    The cache will see that new date and assume the image has been updated and thus requires reprocessing. You should be using the real create date.