
Clone images from your production website using an ImageResizer Plugin
Introduction
My requirement arose because I had updated the development environment to use a recent copy of production and due to various reasons it wasn't feasible to get a copy of the images.
It is assumed that you are already using ImageResizer, and the EPiServerBlobReaderPlugin in your site.
Solution
As my solution used ImageResizer I wanted to see whether I could create a plugin that would allow me to download an asset if it was missing. It turns out that there is an ImageMissing event that you can attach to and this was perfect for my needs.
1public IPlugin Install(Config config)
2{
3 config.Plugins.add_plugin(this);
4 config.Pipeline.ImageMissing += Pipeline_ImageMissing;
5 return this;
6}
7
8private void Pipeline_ImageMissing(IHttpModule sender, HttpContext context, IUrlEventArgs e)
9{
10}
This event allowed me to detect when an image is missing and to download and store in the BlobStorage.
1private void Pipeline_ImageMissing(IHttpModule sender, HttpContext context, IUrlEventArgs e)
2{
3 var productionFile = $"{hostUrl}{e.VirtualPath}";
4 var blobImage = GetBlobFile(e.VirtualPath, e.QueryString);
5
6 using (var c = new HttpClient())
7 {
8 var fileStream = c.GetStreamAsync(productionFile).Result;
9
10 fileStream.CopyTo(blobImage.Blob.OpenWrite());
11 }
12}
13
14private EPiServerBlobFile GetBlobFile(string virtualPath, NameValueCollection queryString)
15{
16 var blobFile = new EPiServerBlobFile(virtualPath, queryString);
17
18 return blobFile;
19}
This worked well until I changed the solution's configuration to store the assets within an Azure Storage Container.
It turns out that Episerver/Optimizely didn't detect that the image was missing; I just saw a lot of 404 error messages when attempting to get the asset from Azure. To handle this, I had to develop another plugin that correctly checks the Azure Storage and then triggers the ImageMissing Event.
1protected virtual CloudBlobContainer GetContainer()
2{
3 return CloudStorageAccount.Parse(this.connectionString).CreateCloudBlobClient()
4 .GetContainerReference(this.container);
5}
6
7public bool FileExists(string virtualPath, NameValueCollection queryString)
8{
9 bool fileExists;
10
11 try
12 {
13 var blobFile = new EPiServerBlobFile(virtualPath, queryString);
14
15 if (blobFile.Blob is AzureBlob)
16 {
17 var cloudBlobContainer = this.GetContainer();
18
19 fileExists = cloudBlobContainer.GetBlobReference(blobFile.Blob.ID.PathAndQuery).Exists();
20 }
21 else
22 {
23 fileExists = blobFile.BlobExists;
24 }
25 }
26 catch
27 {
28 fileExists = false;
29 }
30 return fileExists;
31}
The code above CloudBlobContainer and uses this to check if the file exists; if not, then the ImageMissing event is fired to trigger the downloading of the asset and storing in the Azure Storage Blob.
Configuration
1<plugins>
2 <add name="EPiServerAzureBlobReaderPlugin" />
3 <add name="PatchImagePlugin" azureMode="true|false" hostUrl="https://source.of.images"/>
4</plugins>
You will need to replace the existing plugins with the new ones you have created. You should also use the full namespace rather than just the class name.
azureMode (true|false) = Set to true when writing the assets to Azure Blob storage.
hostUrl = This is the hostname (including protocol) of the site from which you want to download the images.
Conclusion
You can access the full example code from https://gist.github.com/andrewmarkham/473d36fb60229326a11ce32c1fcc9f14
I see this as a useful option when developing, especially when transferring assets between environments is challenging.
I hope the example proves useful, even if it demonstrates how to write a simple Plugin for ImageResizer.