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.

        public IPlugin Install(Config config)
        {
            config.Plugins.add_plugin(this);
            config.Pipeline.ImageMissing += Pipeline_ImageMissing;
            return this;
        }

        private void Pipeline_ImageMissing(IHttpModule sender, HttpContext context, IUrlEventArgs e)
        {

        }

This event allowed me to detect when an image is missing and to download and store in the BlobStorage.

        private void Pipeline_ImageMissing(IHttpModule sender, HttpContext context, IUrlEventArgs e)
        {
            var productionFile = $"{hostUrl}{e.VirtualPath}";
            var blobImage = GetBlobFile(e.VirtualPath, e.QueryString);

            using (var c = new HttpClient())
            {
                var fileStream = c.GetStreamAsync(productionFile).Result;

                 fileStream.CopyTo(blobImage.Blob.OpenWrite());
            }
        }

        private EPiServerBlobFile GetBlobFile(string virtualPath, NameValueCollection queryString)
        {
            var blobFile = new EPiServerBlobFile(virtualPath, queryString);

            return blobFile;
        }

This worked well until I change the configuration of the solution 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 the ImageMissing Event.

        protected virtual CloudBlobContainer GetContainer()
        {
            return CloudStorageAccount.Parse(this.connectionString).CreateCloudBlobClient()
                .GetContainerReference(this.container);
        }

        public bool FileExists(string virtualPath, NameValueCollection queryString)
        {
            bool fileExists;

            try
            {
                var blobFile = new EPiServerBlobFile(virtualPath, queryString);

                if (blobFile.Blob is AzureBlob)
                {
                    var cloudBlobContainer = this.GetContainer();

                    fileExists = cloudBlobContainer.GetBlobReference(blobFile.Blob.ID.PathAndQuery).Exists();

                }
                else
                {
                    fileExists = blobFile.BlobExists;
                }
            }
            catch
            {
                fileExists = false;
            }

            return fileExists;
        }

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

    <plugins>
      <add name="EPiServerAzureBlobReaderPlugin" />
      <add name="PatchImagePlugin" azureMode="true|false" hostUrl="https://source.of.images"/>
    </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 you are writing the assets to Azure Blob storage.
  • hostUrl = this is the hostname (including protocol) of the site you want to download the images from.

Conclusion

You can access the full example code from https://gist.github.com/andrewmarkham/473d36fb60229326a11ce32c1fcc9f14

I see this as a useful option to have when developing, especially when transferring the assets between environments is challenging.

I hope that the example proves useful, even if it demonstrates how to write a simple Plugin for ImageResizer.

Leave a Reply