allBlogsList

Image Optimization - Azure PaaS and Dianoga

Image optimization is an important aspect of ensuring good performance on your site. Often, images are uploaded without optimization in mind, which can cause large traffic loads to heavily impact performance and load times. An edge cache can be used to prevent the round trip call to the server, mitigating some of the impacts, but that may not adjust and optimize the actual size of the image being delivered. There are several tool out there that will optimize images by reducing the overall file size of those images. In my case, I decided to go with Dianoga, an automatic image optimizer that executes the first time an image is requested.

The tool is great and really simple to use: install a NuGet package and you're done. This simplicity does not extend to environments running via Azure Web Services. By default, the optimizers in Dianoga create temporary files in the standard windows temp directory.

protected virtual string GetTempFilePath()
{
    try
    {
        return Path.GetTempFileName();
    }
    catch (IOException ioe)
    {
        throw new InvalidOperationException($"Error occurred while creating temp file to optimize. This can happen if IIS does not have write access to {Path.GetTempPath()}, or if the temp folder has 65535 files in it and is full.", ioe);
    }
}

Unfortunately, the App Services are not going to have access to write to this directory, which means it won't be able to create the temporary files. The optimizers will gracefully fail, and just deliver the default image. This good for ensuring the site doesn't blow up, but does defect the purpose of having the optimizers to begin with. Luckily, it is relatively easy to get around this issue. Kam was nice enough to make the method in question a virtual method, so we just have to extend the original optimizers, having them use a different method to generate their temporary file path.

namespace Foundation.Configuration.Dianoga.DianogaJpeg
{
    public class MozJpegOptimizerOverride : MozJpegOptimizer
    {
        private const string TempFilePrefix = "temp_jpeg";

        protected override string GetTempFilePath()
        {
            try
            {
                var filePath = $"{HttpRuntime.AppDomainAppPath}{FileUtil.GetTemporaryFilePath(TempFilePrefix, "jpeg")}";

                FileUtil.CreateFile(filePath, new MemoryStream(), true);

                return filePath;
            }
            catch (IOException ioe)
            {
                throw new InvalidOperationException($"Error occurred while creating temp file to optimize. This can happen if IIS does not have write access to {FileUtil.GetTemporaryFilePath(TempFilePrefix, "jpeg")}, or if the temp folder has 65535 files in it and is full.", ioe);
            }
        }
    }
}

The above class overrides the JPEG optimizer, and generates a file path based on Sitecore's temporary folder; this folder is used for a couple of things, including creating temporary copies of an image when using the Image Editor. The original class implementation automatically created the temporary file as a part of the original call, but we have to take some extra steps to generate the file using Sitecore's FileUtil class. With the modified class created, it is also necessary to update the configuration to use the proper class when executing the optimizer:

<dianogaOptimizeJpeg>
    <processor type="CNE.Foundation.Configuration.Dianoga.DianogaJpeg.JpegOptimOptimizerOverride, CNE.Foundation.Configuration">
        <ExePath>/App_Data/Dianoga Tools/mozjpeg_3.1_x86/jpegtran.exe</ExePath>
        <AdditionalToolArguments>-progressive</AdditionalToolArguments>
    </processor>
</dianogaOptimizeJpeg>

While I have included an example of one extended class, there are at least three (3) classes that need to be extended for full support:

  • JpegOptimOptimizer
  • PngOptimizer
  • SvgoOptimizer

Each class has a separate config file, as well:

  • Dianoga.Jpeg.config
  • Dianoga.Png.config
  • Dianoga.Svg.config

It's as easy as that! A few extended classes and some config file changes, and Dianoga now works with Azure App Services. Happy coding!