Yesterday I introduced ImageSharp.Web as a solution to integrate image processing capabilities into your ASP.NET Core application
One of the building blocks I talked about where 'Image Providers'. Out-of-the-box there are 3 image providers available:
- PhysicalFileSystemProvider: Loads files from the ‘wwwroot’ folder
- AzureBlobStorageImageProvider: Loads files from Azure Blob Storage
- AWSS3StorageImageProvider: Loads files from AWS S3 Storage
If you want to load files from a different source, you should create your own implementation of the IImageProvider interface.
The images in our application were stored inside the database.
Here are the steps we took to create own implementation.
Implement the IImageProvider interface
First we need to create an implementation of the IImageProvder interface. In this implementation we check if the incoming request can be handled by our Image Provider through the IsMatch property and if so we fetch the data from the database through the GetAsync() method:
public class LoadImageFromDatabaseProvider : IImageProvider | |
{ | |
private readonly IImageRepository _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(IImageRepository 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> | |
if (context.Request.Query.TryGetValue("id", out var value)) | |
{ | |
var id=value.ToString(); | |
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("image"); | |
} | |
} |
Remark: this is a simplified version of our real implementation as we have to add proper error handling when the request URI is not correct.
Implement the IImageResolver interface
We also need to implement the IImageResolver interface. This is used to load the data as a stream and get the necessary metadata:
internal class ImageResolver : IImageResolver | |
{ | |
private byte[] _data; | |
private Metadata _metadata; | |
public ImageResolver(byte[] data, Metadata metadata) | |
{ | |
_data = data; | |
_metadata = metadata; | |
} | |
public Task<ImageMetadata> GetMetaDataAsync() | |
{ | |
return Task.FromResult(new ImageMetadata(_metadata.CreatedOn, _data.Length)); | |
} | |
public Task<Stream> OpenReadAsync() | |
{ | |
return Task.FromResult<Stream>(new MemoryStream(_data)); | |
} | |
} |
Update the configuration to use our own provider
As a last step we need to update the middleware configuration and replace the built-in ImageProvider with our own implementation:
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.AddImageSharp() | |
.ClearProviders() | |
.AddProvider<LoadImageFromDatabaseProvider>(); | |
} |