Using Photo Galleries in Your Web Apps

Introduction

Welcome to this installment of the .NET Nuts & Bolts column. In this article we'll explore the use of images in your web application for eye candy such as profile photos and photo galleries. When using photos in your web application it is important to consider storage requirements and the ability to do a quick preview. We will use classes from the System.Drawing, System.IO.MemoryStream, and others to take a photo, resize it in different sizes and then store it.

Upload an Image

In our scenario we're building a web application that will store and use photos for things like a profile image as well as to have galleries of photos. When an image is uploaded, we'll resize each of the uploaded images to ensure consistent size in addition to reducing the storage size of high resolution images that could be added. In addition, we'll create a thumb nail of the image to display as a preview so you can create a list of images in the gallery without loading the full versions and taking forever for the page to load. For our example I'm assuming the FileUpload or other similar control is being used to handle allowing the user to browse for a photo on their local machine and upload it in to the website. There are a number of examples already for how to upload files in to a website, so we'll skip the actual implementation of this part.

Object to Hold and Store the Images

When you are creating multiple versions of an image you have to consider how you would like to store them. There are a number of choices for saving the image. For this particular example, we'll create a single object that will contain our thumbnail and our full size image. The object that contains both can be serialized. The serialized image can then be stored in a database. Storing the image in a database prevents you from having to replicate the image to multiple servers if you are using a web farm. The following code contains a definition of a simple object that can hold two versions of an image and be serialized for storage.

 using System;
 using System.IO;
 using System.Runtime.Serialization.Formatters.Binary;
 
 namespace Photo
 {
     [Serializable]
     public class PhotoAggregate
     {
         private byte[] _Thumbnail;
         public byte[] Thumbnail
         {
             get { return this._Thumbnail; }
             set { this._Thumbnail = value; }
         }
                 
         private byte[] _WebImage;
         public byte[] WebImage
         {
             get { return this._WebImage; }
             set { this._WebImage = value; }
         }
 
         public PhotoAggregate()
         {
             this._Thumbnail = null;
             this._WebImage = null;
         }
 
         public byte[] Serialize()
         {
             MemoryStream ms = new MemoryStream(100);
             BinaryFormatter bf = new BinaryFormatter();
             bf.Serialize(ms, this);
             return ms.GetBuffer();
         }
 
         public static PhotoAggregate DeSerialize(byte[] blob)
         {
             try
             {
                 MemoryStream ms = new MemoryStream(blob);
                 BinaryFormatter bf = new BinaryFormatter();
                 PhotoAggregate pa = (PhotoAggregate)bf.Deserialize(ms);
                 return pa;
             }
             catch
             {
                 return null;
             }
         }
     }
 }

Resize the Image

The following static method will take an image as input along with the desired new height and width values. Something to keep in mind when resizing an image is the scaling. This example assumes that the height and width should be resized in proportion to each other so the image does not become distorted as it is resized. Based on whether the image is portrait or landscape the appropriate alternate height or width will be calculated to scale. A new Bitmap will be created of the appropriate size. A graphics object will then be extracted from the bitmap and the image redrawn in to the graphics object using the new size. The bitmap is then returned as the output from the method call. Once an image is uploaded to your application you can call this method as many times as you want to create as many different versions in as many different sizes as you want.

 public static System.Drawing.Image ResizeImage(System.Drawing.Image imgPhoto, int Height, int Width)
 {
 int sourceWidth = imgPhoto.Width;
 int sourceHeight = imgPhoto.Height;
 int sourceX = 0;
 int sourceY = 0;
 int destX = 0;
 int destY = 0;
 int destWidth = 0;
 int destHeight = 0;
 
 //Determine the scaling
 if (sourceWidth < Width && sourceHeight < Height)
 {
 destWidth = sourceWidth;
 destHeight = sourceHeight;
 }
 else
 {
 if (sourceHeight > sourceWidth)
 {
 // Portrait Photo - calc scaled width
                   destHeight = Height;
                   float f = sourceWidth;
                   f = f / sourceHeight;
                   destWidth = Convert.ToInt32(Width * f);
 }
 else
 {
                   // Landscape Photo - calc scaled height
                   destWidth = Height;
                   float f = sourceHeight;
                   f = f / sourceWidth;
                   destHeight = Convert.ToInt32(Width * f);
 }
 }
 
 Bitmap bmPhoto = new Bitmap(destWidth, destHeight,
                                   PixelFormat.Format24bppRgb);
 bmPhoto.SetResolution(imgPhoto.HorizontalResolution,
                             imgPhoto.VerticalResolution);
 
 Graphics grPhoto = Graphics.FromImage(bmPhoto);
 grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
 
 grPhoto.DrawImage(imgPhoto,
 new Rectangle(destX, destY, destWidth, destHeight),
 new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight),
 GraphicsUnit.Pixel);
 grPhoto.Dispose();
 return bmPhoto;
 }

Putting it All Together

Using our previously created PhotoAggregate class and the ResizeImage method we are now ready to do some resizing and storeing of our object. The example code below retrieves a JPEG encoder that helps influence the image quality and size, and then uses our ResizeImage() method to create the full size and thumbnail version of our image and save it.

 Photo.PhotoAggregate pa = new MyColts.Net.Photo.PhotoAggregate();
 System.IO.MemoryStream ms;
 System.Drawing.Image newimg;
 System.Drawing.Image img = 
 System.Drawing.Image.FromStream(ImageFile.InputStream);
 
 // Retrieve a JPEG encoder            
 ImageCodecInfo jpegEncoder = null;
 foreach (ImageCodecInfo info in ImageCodecInfo.GetImageEncoders())
 {
 if (info.FormatDescription == "JPEG")
 {
 jpegEncoder = info;
 		break;
 }
 }
 
 // Set the compression parameter of our encoder            
 EncoderParameters parms = new EncoderParameters(1);            
 parms.Param[0] = new EncoderParameter(
 System.Drawing.Imaging.Encoder.Compression, 72);
    
 // Full size image            
 newimg = CroweChizek.Framework.Utility.Util.ResizeImage(img, 560, 560);
 ms = new System.IO.MemoryStream();
 newimg.Save(ms, jpegEncoder, parms);
 pa.WebImage = ms.GetBuffer();
 ms.Dispose();
 ms = null;
 newimg.Dispose();
 
 // Thumbnail
 newimg = CroweChizek.Framework.Utility.Util.ResizeImage(img, 75, 75);
 ms = new System.IO.MemoryStream();
 newimg.Save(ms, jpegEncoder, parms);
 pa.Thumbnail = ms.GetBuffer();
 ms.Dispose();
 ms = null;
 newimg.Dispose();
 
 // Save the images by calling pa.Serialize() and saving it in the database
 // A datatype such as varbinary(max) can be used for the serialized image

Use an HttpHandler to Serve it Up

A suggestion to serve up your images is to create a custom http handler. You can refer to the prior article titled Use Custom HTTP Handlers in Your ASP.NET Applications for details of how to build an http handler. I chose .image as the file extension to run through the handler and included a simple query string parameter tacked on to the end of the image request that indicated whether I wanted the full size or thumb nail version.

Summary

We explored the scenario of using images in your application for things like a photo gallery. We also looked at how to use an object to store multiple versions and how to resize an image to create the multiple versions to store. We then considered using an http handler to serve them up and made reference to another article on how to create the http handler.



About the Author

Mark Strawmyer

Mark Strawmyer is a Senior Architect of .NET applications for large and mid-size organizations. He specializes in architecture, design and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C# for the fifth year in a row. You can reach Mark at mark.strawmyer@crowehorwath.com.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds