Asynchronous resize of images (async http handler) with default sitecore image

Images can usually take up most of your page load time. In our project we were loading product images from the servers, and resizing them before serving them up on the site. To improve performance, we decided to try and do the resizing and image loading there after asynchoronously.
So we created an asynchronous http handler to perform this action.

We were resizing external images (not stored in sitecore), but we did want to load up a default image stored in sitecore if the image didn’t exist on the filesystem.
To achieve this, we passed in the url of the default image (language specific) as a querystring parameter to the http handler itself. In this case, it would check if the intended image existed on the filesystem, if not, it would return the resized default image in the response stream.

using Sitecore.Diagnostics;
using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Threading;
using System.Web;

namespace MySite.Customizations
{
    class ImageResizer : IHttpAsyncHandler
    {
        public bool IsReusable { get { return false; } }

        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
        {
            ImageProcessor asyncImgProcessor = new ImageProcessor(cb, context, extraData);
            asyncImgProcessor.StartAsyncWork();
            return asyncImgProcessor;
        }

        public void EndProcessRequest(IAsyncResult result)
        {
        }

        public void ProcessRequest(HttpContext context)
        {
        }
    }

    class ImageProcessor : IAsyncResult
    {
        private bool _completed;
        private Object _state;
        private AsyncCallback _callback;
        private HttpContext _context;

        bool IAsyncResult.IsCompleted { get { return _completed; } }
        WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } }
        Object IAsyncResult.AsyncState { get { return _state; } }
        bool IAsyncResult.CompletedSynchronously { get { return false; } }

        public ImageProcessor(AsyncCallback callback, HttpContext context, Object state)
        {
            _callback = callback;
            _context = context;
            _state = state;
            _completed = false;
        }

        public void StartAsyncWork()
        {
            ThreadPool.QueueUserWorkItem(StartAsyncTask, null);
        }

        private void StartAsyncTask(Object workItemState)
        {
            if (_context.Request.QueryString["path"] != null)
            {
                try
                {
                    string imagePath = _context.Request.QueryString["path"];

                    if (string.IsNullOrWhiteSpace(imagePath))
                    {
                        EndAsyncTask();
                        return;
                    }

                    imagePath = HttpUtility.Decode(imagePath);
                    Image image = null;
                    bool isFileSystem = false;

                    string fileSystemImagePath;

                    if (!ImageHelper.IsValidProductFileSystemImage(imagePath, out fileSystemImagePath))
                    {
                        imagePath = _context.Request.Url.Scheme + "://" + _context.Request.Url.Host
                            + HttpUtility.Decode(_context.Request.QueryString["defaultimage"]);
                    }
                    else
                    {
                        image = Image.FromFile(fileSystemImagePath);
                        isFileSystem = true;
                    }

                    int h = 0, w = 0;
                    if (_context.Request.QueryString["h"] != null)
                    {
                        h = Convert.ToInt32(string.IsNullOrWhiteSpace(_context.Request.QueryString["h"]) ? "0" : _context.Request.QueryString["h"]);
                    }
                    if (_context.Request.QueryString["w"] != null)
                    {
                        w = Convert.ToInt32(string.IsNullOrWhiteSpace(_context.Request.QueryString["w"]) ? "0" : _context.Request.QueryString["w"]);
                    }

                    try
                    {
                        if (!isFileSystem)
                        {
                            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(imagePath);
                            using (WebResponse response = request.GetResponse())
                            using (HttpWebResponse httpResponse = (HttpWebResponse)response)
                            {
                                if (httpResponse.StatusCode != HttpStatusCode.OK)
                                {
                                    EndAsyncTask();
                                    return;
                                }
                                using (Stream imageStream = response.GetResponseStream())
                                {
                                    if (imageStream == null)
                                    {
                                        EndAsyncTask();
                                        return;
                                    }

                                    image = Image.FromStream(imageStream);
                                }
                            }
                        }

                        byte[] imageBytes = ImageHelper.ResizeAndCropImage(image, w, h,
                            (_context.Request.QueryString["crop"] == null || _context.Request.QueryString["crop"] == "true"));

                        image.Dispose();

                        _context.Response.ContentType = "image/jpeg";
                        if (imageBytes != null)
                        {
                            _context.Response.BinaryWrite(imageBytes);
                        }
                        _context.Response.End();
                    }
                    catch (Exception ex)
                    {
                        Log.Error(ex.Message, ex);
                        EndAsyncTask();
                        return;
                    }
                }
                catch (Exception ex)
                {
                    Log.Error("Exception in fetching product image for resize: " + ex.StackTrace, ex, this);
                }
            }
            EndAsyncTask();
        }

        public void EndAsyncTask()
        {
            _completed = true;
            _callback(this);
        }
    }
}

In the above code, IsValidProductFileSystemImage – basically executes a System.IO.File.Exists call to check whether the image in the ‘path’ parameter exists.
Additionally, the code for ResizeAndCropImage can be found at: Resize Images with Crop and Quality Control.

Advertisements

, , , , , ,

  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: