Monday, November 20, 2017

Display Images Using Generic Handler in ASP.NET MVC

There are several options on how to show images in ASP.NET MVC. One of them is using a generic handler. This method was applied since the early days of ASP.NET Webforms and still can be used with recent frameworks. The class below will render the images on the page by calling the ProcessRequest method which will then get the image from datasources such as database and file system and return it as a Bitmap object.
public class ImageHandler : IHttpHandler
{
 private OnlineShoppingDBEntities shoppingContext;

 public ImageHandler()
 {
  shoppingContext = new OnlineShoppingDBEntities();
 }

 public void ProcessRequest(HttpContext context)
 {
  if (context.Request.QueryString["ProductID"] != null)
  {
   Product product = shoppingContext.Products.Find(Convert.ToInt32(context.Request.QueryString["ProductID"]));
   if (product != null)
   {
    Byte[] bytes = product.Image;
    int height = 0;
    int width = 0;

    height = 100;
    width = 100;

    Bitmap image = GetImage(bytes, height, width);

    context.Response.Buffer = true;
    context.Response.Charset = "";
    context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    context.Response.ContentType = "image/jpeg";
    image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
    context.Response.Flush();
    context.Response.End();
   }
  }
 }

 private Bitmap GetImage(Byte[] bytes, int width, int height)
 {
  Image originalImage;
  using (MemoryStream ms = new MemoryStream(bytes))
  {
   originalImage = Image.FromStream(ms);
  }

  var newImage = new Bitmap(originalImage, width, height);
  Graphics g = Graphics.FromImage(originalImage);
  g.FillRectangle(Brushes.White, 0, 0, newImage.Width, newImage.Height);
  Rectangle Box = new Rectangle(10, 10, newImage.Size.Width - 20, newImage.Size.Height - 20);
  g.DrawRectangle(Pens.Black, Box);
  return newImage;
 }

 public bool IsReusable
 {
  get
  {
   return false;
  }
 }
}
When using this code to show images of type GIF, you might encounter an issue such as "A Graphics object cannot be created from an image that has an indexed pixel format". The fix for that is to revise the GetImage() method wherein you create a blank bitmap with the same dimensions and the correct PixelFormat and then draw on that bitmap the original image bitmap.
public class ImageHandler : IHttpHandler
{

 ProductRepository repository;

 public ImageHandler()
 {
  repository = new ProductRepository();
 }

 public void ProcessRequest(HttpContext context)
 {
  if (context.Request.QueryString["ProductID"] != null)
  {
   ProductViewModel product = repository.Product(Convert.ToInt32(context.Request.QueryString["ProductID"]));
   if (product != null)
   {
    Byte[] bytes = product.Image;
    Bitmap image = GetImage(bytes);

    context.Response.Buffer = true;
    context.Response.Charset = "";
    context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    context.Response.ContentType = "image/jpeg";
    image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
    context.Response.Flush();
    context.Response.End();
   }
  }
 }

 private Bitmap GetImage(Byte[] bytes)
 {
  Image originalImage;
  Bitmap originalBmp;
  using (MemoryStream ms = new MemoryStream(bytes))
  {
   originalImage = Image.FromStream(ms);
  }

  originalBmp = new Bitmap(originalImage);
  Bitmap tempBitmap = new Bitmap(originalBmp.Width, originalBmp.Height);
  using (Graphics g = Graphics.FromImage(tempBitmap))
  {
   // Draw the original bitmap onto the graphics of the new bitmap
   g.DrawImage(originalBmp, 0, 0);
   Rectangle Box = new Rectangle(0, 0, tempBitmap.Size.Width, tempBitmap.Size.Height);
   g.DrawRectangle(Pens.White, Box);
  }

  return tempBitmap;
 }

 public bool IsReusable
 {
  get
  {
   return false;
  }
 }
}
Another modification to the class below will solve the issue on displaying images with OLE header which will check if the image format is of type RawFormat.
public class ImageHandler : IHttpHandler
{
 private NorthwindEntities _context;

 public ImageHandler()
 {
  _context = new NorthwindEntities();
 }
 
 public void ProcessRequest(HttpContext context)
 {
  if (context.Request.QueryString["EmployeeID"] != null)
  {
   var employee = _context.Employees.Find(Convert.ToInt32(context.Request.QueryString["EmployeeID"]));
   if (employee != null)
   {
    Byte[] bytes = employee.Photo;
    Bitmap image = GetImage(bytes);

    context.Response.Buffer = true;
    context.Response.Charset = "";
    context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    context.Response.ContentType = "image/jpeg";
    image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
    context.Response.Flush();
    context.Response.End();
   }
  }
 }

 private Bitmap GetImage(Byte[] bytes)
 {
  Image originalImage;
  Bitmap originalBmp;

  using (MemoryStream ms = new MemoryStream(bytes))
  {
   Image xImage = (Bitmap)((new ImageConverter()).ConvertFrom(bytes));
   if (ImageFormat.Bmp.Equals(xImage.RawFormat))
   {
    ms.Write(bytes, 78, bytes.Length - 78);
   }
   else
   {
    ms.Write(bytes, 0, bytes.Length);
   }

   originalImage = Image.FromStream(ms);
  }

  originalBmp = new Bitmap(originalImage);
  Bitmap tempBitmap = new Bitmap(originalBmp.Width, originalBmp.Height);
  using (Graphics g = Graphics.FromImage(tempBitmap))
  {
   // Draw the original bitmap onto the graphics of the new bitmap
   g.DrawImage(originalBmp, 0, 0);
   Rectangle Box = new Rectangle(0, 0, tempBitmap.Size.Width, tempBitmap.Size.Height);
   g.DrawRectangle(Pens.White, Box);
  }

  return tempBitmap;
 }

 public bool IsReusable
 {
  get
  {
   return false;
  }
 }
}
To use this class, all you need to do is add an img tag in your view and replace the src attribute with the relative path of the handler and append the model id as query string.
<img src="~/Helpers/ImageHandler.ashx?ProductID=@item.ProductID" />
Rendered View:

0 comments:

Post a Comment