Image copyright TinyPNG.comWe all know that large image size is a killer for site performance. So it's always recommended that images are optimised. Episerver already has the awesome ImageResizer plugin for Episerver which can be used to compress and resize images.

However, when working on some recent proof of concepts it was noticed that the ImageResizer plugin did not perform too well with transparent PNGs. One of best tools around for compressing PNG's is TinyPNG. On testing it particularly seems to excel with transparent PNG's. So this blog shares some code that shows how it's possible to optimise image size on upload using the TingPNG.com API:

using System;
using System.IO;
using System.Net;
using System.Text;
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAccess;
using EPiServer.Framework;
using EPiServer.Framework.Blobs;
using EPiServer.Framework.Initialization;
using EPiServer.Security;
using EPiServer.ServiceLocation;
using TinyPngPOC.Models.Media;

namespace TinyPngPOC.Business.Initialization
{
    [InitializableModule]
    [ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class ImageOptimiseInit : IInitializableModule
    {
        public void Initialize(InitializationEngine context)
        {
            var contentEvents = ServiceLocator.Current.GetInstance();
            contentEvents.CreatedContent += Optimise;
        }
        private void Optimise(object sender, ContentEventArgs e)
        {
            try
            {
                if (!(e.Content is ImageData)) return;

                var repo = ServiceLocator.Current.GetInstance();
                if (repo == null) return;

                var image = (ImageData)e.Content;
                if (image == null) return;

                var extension = Path.GetExtension(image.Name);
                if (extension.Equals(".PNG", StringComparison.InvariantCultureIgnoreCase) || 
                    extension.Equals(".JPG", StringComparison.InvariantCultureIgnoreCase))
                {
                    OptimiseImage(image);
                }
            }
            catch (Exception ex)
            {
                e.CancelAction = true;
                e.CancelReason = ex.ToString();
            }
        }

        private void OptimiseImage(ImageData image)
        {
            const string key = "";
            const string url = "https://api.tinify.com/shrink";

            var client = new WebClient();
            var auth = Convert.ToBase64String(Encoding.UTF8.GetBytes("api:" + key));
            client.Headers.Add(HttpRequestHeader.Authorization, "Basic " + auth);

            byte[] originalImage;
            using (var memoryStream = new MemoryStream())
            {
                image.BinaryData.OpenRead().CopyTo(memoryStream);
                originalImage = memoryStream.ToArray();
            }

            client.UploadData(url, originalImage);
            var optimisedImage = client.DownloadData(client.ResponseHeaders["Location"]);

            var blobFactory = ServiceLocator.Current.GetInstance();

            var writeableImage = image.CreateWritableClone() as ImageFile;
            if (writeableImage != null)
            {
                var blob = blobFactory.CreateBlob(writeableImage.BinaryDataContainer, Path.GetExtension(image.Name));
                using (var s = blob.OpenWrite())
                {
                    var b = new BinaryWriter(s);
                    b.Write(optimisedImage);
                    b.Flush();
                }
                writeableImage.BinaryData = blob;

                var repo = ServiceLocator.Current.GetInstance();
                repo.Save(writeableImage, SaveAction.Patch, AccessLevel.NoAccess);
            }
        }

        public void Uninitialize(InitializationEngine context)
        {
            var contentEvents = ServiceLocator.Current.GetInstance();
            contentEvents.CreatedContent -= Optimise;
        }
    }
}

Conclusion

This is just a quick example to show how the TinyPNG API can be used to automatically optimise images when they are uploaded in Episerver, it doesn't take image resizing into account. I think it would be great to wire this in as an extension to ImageResizer plug in for Episerver but that's another blog!


Comments

comments powered by Disqus