Automatically Resize, Link, and Compress Images Using Sitecore

We had a client who wanted us to redesign their blog website. After having a discussion with the design team, we realized that the existing article images were not correctly sized, and that we had to create multiple aspect ratios for images to be hosted on various devices, like desktops, and mobile, tablet, and Retina-enabled devices.

Another requirement was we had to compress the images so they could render better in low bandwidth devices. The client was fine with manually uploading images because there were thousands of articles and each article had more than 10 images. However, after some time we realized that we also needed to create specifically sized images for the article widgets, but this size ratio could not be determined until the design of the site was finalized. We then had to take over uploading all of the images to the articles.

Considering these points, we started thinking about automating the resizing and compression of images. We used the Sitecore CMS to manage the content of the site, as well as all of our images on the site. Initially, we created a utility to resize the images on the fly by supplying the height and width. We stored high-resolution images with every aspect ratio in the CMS and the utility was able to resize and compress all of them. However, there were issues with this approach. First, the on-the-fly processing was taking time, especially when it was rendering pages with a multitude of images. Second, we lost the CDN advantage and kept resizing the same images again and again in each article request.

After going through this, we realized that it was better to resize and compress every image ratio first, and then store them in the Sitecore image fields to deliver when requested. This allowed us to increase our performance and regain the CDN advantage.

Major Benefits of our Solution

We were able to generate compressed images of different sizes, such as a 6 MB image in 350 KBS with a high-quality display.

We reduced almost 80% of the time of the Sitecore content manager.

We achieved faster article page rendering using the device-based respective image size.

Flow Chart

sitecore1

Screenshot of the Resized Images in Fields

sitecore2

Steps Performed to Accomplish The Task

1. Input the article image fields in Sitecore for every aspect ratio, as well as a respective Boolean flag to turn the resizing on and off. For example:

RatioImage Fields to link (high resolution images)Boolean Flag to turn resizing on and off
1:1Square_2880x2880Square_1920x1920Square_960x960SquareImageResizeAndLink
2:1Wide_4320x4320Wide_2880x1440Wide_1920x960Wide_960x480WideImagesResizeAndLink
8:3Extrawide_4320x1620Extrawide_2880x1080Extrawide_1920x720Extrawide_960x360ExtraWideImagesResizeAndLink

The Boolean flag will turn off after the image is resized so as to avoid accidentally triggering while the item is saving.

2. Create a configuration file to manage image resizing, with specific templates and fields with dimension details.

<CMS.resizeAndLinkImages>
  <ResizeAndLinkImages>
	<ResizeAndLinkImage TemplateIds="{3A29EA5F-26E6-4F1B-B2FB-0EF633F4ECBE}">
  	<MainImages>
    	<MainImage FieldName ="Square_2880x2880" FlagFieldName="SquareImagesResizeAndLink">
      	<LinkToImages>
        	<LinkToImage FieldName="Square_1920x1920" Height="1920" Width="1920"/>
        	<LinkToImage FieldName="Square_960x960" Height="960" Width="960"/>
        	<LinkToImage FieldName="Square_2880x2880" Height="2880" Width="2880"/>
      	</LinkToImages>
    	</MainImage>
    	<MainImage FieldName ="Wide_4320x2160" FlagFieldName="WideImagesResizeAndLink">
      	<LinkToImages>
        	<LinkToImage FieldName="Wide_2880x1440" Height="1440" Width="2880"/>
        	<LinkToImage FieldName="Wide_1920x960" Height="960" Width="1920"/>
        	<LinkToImage FieldName="Wide_960x480" Height="480" Width="960"/>
        	<LinkToImage FieldName="Wide_4320x2160" Height="2160" Width="4320"/>
      	</LinkToImages>
    	</MainImage>
    	<MainImage FieldName ="Extrawide_4320x1620" FlagFieldName="ExtraWideImagesResizeAndLink">
      	<LinkToImages>
        	<LinkToImage FieldName="Extrawide_2880x1080" Height="1080" Width="2880"/>
        	<LinkToImage FieldName="Extrawide_1920x720" Height="720" Width="1920"/>
        	<LinkToImage FieldName="Extrawide_960x360" Height="360" Width="960"/>
        	<LinkToImage FieldName="Extrawide_4320x1620" Height="1620" Width="4320"/>
      	</LinkToImages>
    	</MainImage>
  	</MainImages>
	</ResizeAndLinkImage>
</CMS.resizeAndLinkImages>

3. Write both a class and methods by using Sitecore DLLs and the .Net system. Draw namespace to resize and link images in the Sitecore content items. There is a code snippet below for reference.

The main methods for this are:

  • OnItemSaved: An OnItemSaved event is an entry point to trigger the resize feature. It takes the Sitecore item as EventArgs and manages the editing of the item by using other helper functions like reading configuration, call auto resize, and create new media item function.
  • CompressAndResizeImage: This function returns the byte array of a resized image and inputs the stream, new height, new width, and an image format.
  • CreateMediaItem: This function is used to create a new media item by supplying the stream, CMS path, file name, extension, and CMS DB name.
public void OnItemSaved(object sender, EventArgs args)
    	{
        	global::Sitecore.Events.SitecoreEventArgs eventArgs = args as global::Sitecore.Events.SitecoreEventArgs;
        	global::Sitecore.Diagnostics.Assert.IsNotNull(eventArgs, "eventArgs");
        	global::Sitecore.Data.Items.Item item = eventArgs.Parameters[0] as global::Sitecore.Data.Items.Item;
        	global::Sitecore.Diagnostics.Assert.IsNotNull(item, "item");
 
        	if (item.Database != null && String.Compare(item.Database.Name, this.Database) != 0)
        	{
            	return;
        	}
        	if (_inProcess.Contains(item.ID))
        	{
            	return;
        	}
 
            	var configuration =
                	(ImageResizingConfiguration)ConfigurationManager.GetSection("CMS.resizeAndLinkImages");
                	// fetch configuration value of saved containt item template if any
            	ResizeAndLinkImageElement itemResizeAndLinkImageElement = configuration.ResizeAndLinkImageElementCollection.FirstOrDefault(itemResizeAndLinkImage => itemResizeAndLinkImage.TemplateIds.Contains(item.TemplateID.ToString()));
 
            	global::Sitecore.Diagnostics.Assert.IsNotNull(item.TemplateID, "item.TemplateID");
            	if (itemResizeAndLinkImageElement != null)
            	{
                	_inProcess.Add(item.ID);
                	try
                	{
                    	using (new global::Sitecore.SecurityModel.SecurityDisabler())
                    	{
                        	try
                        	{
                            	// start editing item
item.Editing.BeginEdit();
                            	
                            	foreach (var itemResizeAndLinkImage in itemResizeAndLinkImageElement.MainImages)
                            	{
                                    Sitecore.Data.Fields.CheckboxField flagFieldName =
                                        item.Fields[itemResizeAndLinkImage.FlagFieldName];
                                	if (flagFieldName.Checked)
                                	{
                                        flagFieldName.Checked = false;
                                        Sitecore.Data.Fields.ImageField mainImageFieldName =
                                            item.Fields[itemResizeAndLinkImage.FieldName];
                                    	if (mainImageFieldName != null && mainImageFieldName.MediaItem != null)
                                    	{
                                        	var mainImage =
                                                new Sitecore.Data.Items.MediaItem(mainImageFieldName.MediaItem);
 
                                        	foreach (var itemLinkToImages in itemResizeAndLinkImage.LinkToImages)
                                        	{
                                                string newMediaItemName = string.Format("{0}_{1}_{2}", mainImage.Name,itemLinkToImages.Width.ToString(),itemLinkToImages.Height.ToString ());
                                                string newMediaSiteCorePath = GetSitecorePath(mainImage.Path, mainImage.Name);
                                                Stream stream =
                                                    new MemoryStream(CompressAndResizeImage(mainImage.GetMediaStream(),
      	                                                                       itemLinkToImages.Width, itemLinkToImages.Height, GetImageFormat(mainImage.Extension)));
                                            	// create new media item
            	                                var newMediaItem = CreateMediaItem(stream, newMediaItemName, newMediaSiteCorePath, mainImage.Extension, this.Database);
 
                                                Sitecore.Data.Fields.ImageField imageFieldToLink =
                                                    item.Fields[itemLinkToImages.FieldName];
                                            	// link image field to media
 
                                                imageFieldToLink.MediaID = newMediaItem.ID;
                                        	}
 	                                   }
                                	}
                            	}
                        	}
                        	finally
                        	{
                                item.Editing.EndEdit();
                        	}
                    	}
                	}
                	finally
                	{
                        _inProcess.Remove(item.ID);
                	}
            	}
    	}
 
 
    	public MediaItem CreateMediaItem(System.IO.Stream image, string mediaItemName, string sitecorePath, string extension, string databaseName)
    	{
        	var options = new Sitecore.Resources.Media.MediaCreatorOptions
            	{
                	AlternateText = mediaItemName,
                	FileBased = false,
                	IncludeExtensionInItemName = false,
                	KeepExisting = false,
                	Versioned = false,
                	Destination = sitecorePath + mediaItemName,
                	Database = Sitecore.Configuration.Factory.GetDatabase(databaseName)
            	};
 
        	var creator = new MediaCreator();
        	var mediaItem = creator.CreateFromStream(image, sitecorePath + mediaItemName + extension, options);
        	return mediaItem;
    	}
 
 
    	public byte[] CompressAndResizeImage(System.IO.Stream imageStream, int width, int hight, System.Drawing.Imaging.ImageFormat imageFormat)
    	{
        	try
        	{
            	using (var originalImage = System.Drawing.Image.FromStream(imageStream))
            	{
                	// calculate aspect ratio
                	float xRatio = (float)width / (float) originalImage.Width;
                	float yRatio = (float)hight / (float) originalImage.Height;
                	float ratio = Math.Min(xRatio, yRatio);
 
                	// calculated width and height based on aspect ratio
                	int newWidth = (int)( originalImage.Width * ratio);
                	int newHeight = (int)( originalImage.Height * ratio);
 
                	using (var resizeImage = new System.Drawing.Bitmap(originalImage, newWidth, newHeight))
                	{
                    	using (Graphics newGraphics = Graphics.FromImage(resizeImage))
                    	{
                        	newGraphics.CompositingQuality = CompositingQuality.HighQuality;
                        	newGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                        	newGraphics.SmoothingMode = SmoothingMode.HighQuality;
                        	newGraphics.DrawImage(resizeImage, 0, 0, newWidth, newHeight);
                    	}
 
                    	using (var stream = new System.IO.MemoryStream())
                    	{
                        	resizeImage.Save(stream, imageFormat);
                        	return stream.GetBuffer();
                    	}
                	}
            	}
        	}
        	catch (Exception exp)
        	{
            	Log.Error(exp);
        	}
 
        	return null;
    	}

4. The Sitecore configuration changes to register and trigger the resize class in the OnItemSaved method.

<event name="item:saved">
    	<handler type="Sitecore.Admin.Web.Interface.ResizeImage, Sitecore.Admin.Web.Interface" method="OnItemSaved" >
    	<database>master</database> 	
    	</handler>
  	</event>

After all of this was done, we demonstrated it to the client with Sitecore users running through with some article images and the design team integrating new images into the site. The client highly appreciated the way we automated and achieved the goal, as it would have been a painstaking and costly process to manage manually.

Dinesh Verma

Dinesh Verma

Senior Technical Lead

Dinesh Kumar Verma is a Senior Technical Lead in 3Pillar’s Noida, India office. Dinesh specializes in web development using Microsoft technologies. He has over ten years of adept experience in web-based business application architecting, designing, development, and support. His specialties include C#.NET, ASP.NET, MVC, SQL Server, Sitecore, and Web API. In his present role, he develops and manages software solutions with a team for various business needs using the Agile methodology.

Leave a Reply

Related Posts

The 3 Keys to Building Products That Drive Retention –... I had the privilege of being invited to speak at the Wearable Technology Show in Santa Clara this week, where I gave a bit of a reprisal of a talk I d...
High Availability and Automatic Failover in Hadoop Hadoop in Brief Hadoop is one of the most popular sets of big data processing technologies/frameworks in use today. From Adobe and eBay to Facebook a...
3Pillar CEO David DeWolf Quoted in Enterprise Mobility Excha... David DeWolf, Founder and CEO of 3Pillar Global, was recently quoted in a report by Enterprise Mobility Exchange on the necessity of understanding and...
How the Right Tech Stack Fuels Innovation – The Innova... On this episode of The Innovation Engine podcast, we take a look at how choosing the right tech stack can fuel innovation in your company. We'll talk ...
The Road to AWS re:Invent 2018 – Weekly Predictions, P... For the last two weeks, I’ve been making predictions of what might be announced at AWS’ upcoming re:Invent conference. In week 1, I made some guesses ...