Playing with Images in ASP.NET 3.5 AJAX Applications

In this long article, you will learn nearly all the image related problems under the newest ASP.NET 3.5 AJAX environments, and how to deal with them. This article is the first of two parts.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 12
August 26, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

First, to test the sample applications provided in this article, it is highly recommended that you install the following:

  • Windows XP Professional or higher versions (built-in support for GDI+ 1.0).
  • Microsoft Visual Studio 2008 (ASP.NET 3.5 provided the built-in support for ASP.NET AJAX Extensions).
  • Microsoft SQL Server 2005 or higher, with the sample database AdventureWorks_Data.mdf installed.
  • ASP.NET AJAX Control Toolkit, which is required for the last sample in this article.

Second, to follow along with all the samples in this article, it is strongly suggested that you download the source files accompanying this article.

Finally, because the sample database AdventureWorks_Data.mdf is huge (more than 160Mb), I have removed it in the source code that comes with this article.

Introduction

Nowadays, images are spreading all over the Internet. Can you image what the Internet without image support would look like? Therefore, image, as with any other kind of data, is becoming more of a necessary item for nearly all ASP.NET developers. However, unlike the inner workings with general Windows desktop applications, due to the limited bandwidth of the current Internet and the no-state characteristic of HTTP protocol, rendering images in an ASP.NET application usually requires a special image toolkit or third party support. Delightfully, with the new image engine of GDI+ and the earthshaking ASP.NET 2.0 coming into the market, ASP.NET developers can easily deal with images in the web application scenario.

On the one hand, it is clear that images aren't always deployed in the web field. On the other hand, in many web applications, images play a crucial role, as important as any other kind of data in web applications such as tour guides, finance and economics analyses, and photography exhibitions. In these cases, these applications probably require image data to be generated and rendered dynamically, or processed with a distinguishing mark (such as a watermark or copyright information).

On the other hand, GDI+ is the new interface for drawing Windows graphics, which drastically simplifies the image process. It used to required complex and additional work in the days of plain GDI, but with GDI+ came a set of new concepts and classes.  In the old days, to deal with images flexibly, you often have to fall back on the third party's web image toolkit or libraries. Thanks to GDI+, ASP.NET developers can now accomplish nearly any image-associated tasks individually without needing additional image tools or libraries.

In this article, I will lead you systematically through nearly all the image process related concerns under the ASP.NET 3.5 environment through several typical samples, especially laying emphasis upon GDI+ support with process images. What's more, you can learn how to deal with images under popular AJAX environments in combination with Microsoft's ASP.NET 3.5.

Hacking the HTML <img> element

On the whole, there is merely one way to refer to an image in an HTML page-using the HTML <img> element. The following lists the simple and typical form of usage for the <img> mark:

<img id="ImgID"
alt="alternate text for the image"
align="top|middle|bottom|left|right"
border="border width"
height="image height"
src="the url to the special image file"
width="image width" />

Author's Note: in the case of Microsoft Internet Explorer, the <img> mark is designed to embed both an image and a video clip in a web page. When using the img element to display a static image, specify the URL of the image file with the src attribute. When using the img element to display a video clip or virtual reality modeling language (VRML) world, specify the URL with the dynsrc attribute. Regrettably, due to the well-known incompatibility between browsers, Mozilla Firefox does not support the video clip functionality.

To show an image at the web page you have to provide the corresponding URL that identifies an image. In many cases, the URL points to a static image resource, such as an .gif, .png or .jpeg file. The following concludes the entire image and video file formats supported by IE:

.avi-Audio-Visual Interleaved (AVI)

.bmp-Windows Bitmap (BMP)

.emf-Windows Enhanced Metafile (EMF)

.gif-Graphics Interchange Format (GIF)

.jpg, .jpeg-Joint Photographic Experts Group (JPEG)

.mov-Apple QuickTime Movie (MOV)

.mpg, .mpeg-Motion Picture Experts Group (MPEG)

.png-Portable Network Graphics (PNG)

.wmf-Windows Metafile (WMF)

Note in the "static" resource scenarios, the web server can serve incoming requests of this kind internally, independent of the out-most components. However, this is only one of the cases; the image data in practical situations for the <img> element may reside in other kinds of storage media, such as databases, .NET assemblies, or even the memory of the web servers.

In ASP.NET, the HtmlImage class is utilized to describe and render the <img> mark; however, besides providing more flexible control over the images, this merely means one of many possible ways to create the <img> mark. Another apparent alternate method to rendering an image on a web page is to leverage the original html <img> element.

Digging more into the inner workings behind the exterior <img> mark, we should notice that the URL of an <img> mark can point to more than an static and direct path of an image in order to render an image on the web page. Suppose the URL points to an .aspx page. The web server will run the specified page and dispatch the final result to this attribute. Experiments show that as long as this result is pointing to some valid image format, it can be rendered onto the screen.

A Few words about the HTTP Handler

Since the HTTP Handler is almost necessary in all the following sample applications, let's say a few words about it.

HTTP handlers and modules are basic components of Microsoft's ASP.NET architecture. Any request for ASP.NET-managed resources will be resolved using an HTTP handler, and then transmitted through an HTTP module. For example, ASP.NET will map, per incoming HTTP request, to a special HTTP handler.

ASP.NET has built into it several ready-made HTTP handlers. There is one special HTTP handler to serve the ASP.NET pages, one to serve the .NET Web services and one to resolve the .NET remote requests for the IIS managed remote objects. There are also many other auxiliary handlers, such as trace.axd to trace the page life cycle, webresource.axd to inject assembly resources and scripts into the images, etc.

In other conditions, you may ask ASP.NET to process some request in the non-standard means when you can write your custom HTTP handlers. In fact, you can rely on the custom HTTP handlers to do any jobs. For example, via an elaborate custom HTTP handler, you can let the users make nearly any invocation through the web, such as to write a click counter, to deal with any image related tasks, etc.

Now, we've talked enough about theory. Let us roll up our sleeves to construct some interesting sample applications.

Using a .aspx Page to Render Images

From the point of view of a browser, a web page is merely a series of characters waiting in line to be processed. When the browser finds an <img> mark it comes to realize that it needs to open another related download channel and sends out a request for the specified URL. To the browser, it does not matter much what kinds of URL and protocol they are; what really concerns it is that the expected output will match a given format- the MIME type.

Let's examine a trivial example to demonstrate how to specify the proper MIME type using the Content-Type header to render an image onto the page indirectly.

First, let us look at the main page ( AspxMode.aspx ) to undertake the task of rendering the image. The following gives the key piece of code:

<div>

<asp:Image ID="Image1" runat="server" ImageUrl="pic.aspx" />

</div>

Here, when the browser finds the <img> mark (which is translated from the ASP.NET server control <Image>), it will open another download channel as well as send out the related request. Then, it expects the MIME type to be matched. If so, an image will be output; otherwise, some exception will possibly be thrown.

Now, let's see what hides within the other page pic.aspx. The HTML elements' definitions are listed as below:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="pic.aspx.cs" Inherits="pic" %>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">

<title>Untitled Page</title>

</head>

<body>

<form id="form1" runat="server">

<div>

</div>

</form>

</body>

</html>

Anything peculiar? Nothing, but Visual Studio 2008 automatically generated stuff! In fact, what really plays the crucial role lies in the code-behind file-pic.aspx.cs:

public partial class pic : System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

string s = Server.MapPath(@"~/imgs/lord.bmp");

Response.ContentType = "image/bmp";

Response.WriteFile(s);

}

}

Here, we first get the path to the image file lord.bmp, and after we set the ContentType attribute to "image/bmp," we simply write the image data bits to the response (output) stream. Note, in later samples, we will do something with the image bytes before writing them to the stream. Figure 1 shows the running-time snapshot of the AspxMode.aspx page.


Figure 1-the running-time snapshot for AspxMode.aspx

In fact, if you try to start up the pic.aspx page independently, you will still catch sight of the same picture. In the latter case, the web server produces the correct image bytes, sets the proper content type, and finally writes all data into the output stream. While in the former case, the output stream in relation to the  pic.aspx page is further redirected to the src attribute of the <img> element in the first AspxMode.aspx page, and as a result, the AspxMode.aspx page takes the responsibilities of rendering the image onto the screen.

Author's Note: an aspx page is in fact a complex HTTP handler. Although this sample has achieved the aim of rendering an image via another aspx page acting as an agency, this is not the recommended way to output an image dynamically. The mostly recommended way to accomplish such tasks is to define a custom HTTP handler. In later samples, we will delve into it.

Next, let us see another way to load images.

Loading Images from an Assembly

As we have mentioned previously, the image data for the <img> mark can be provided by several means, such as the .NET assemblies, databases, or even by dynamic means. In this section, we will discuss the assembly solution, i.e. persisting image data in system or custom assemblies.

Let us first look at the running-time snapshot for the AccessDll.aspx page, as is shown in Figure 2.


Figure 2-the running-time snapshot for the assembly solution

When designing the AccessDll.aspx page, we dragged the two important ASP.NET AJAX server side controls (ScriptManager and UpdatePanel) into the page. To achieve the partially updating effect, we've leveraged the UpdatePanel control to enclose the image area, with button labeled "Show the image in Ajax Way" as the AsyncPostBackTrigger type of trigger. For example, when you select one of the image files from the above ListBox control and press the "Show the image in Ajax Way" button, the bottom image area (in this case an ASP.NET server control Image) will display the selected image in the AJAX way. Note that the selected image data are provided by a server-side assembly named MyImageDll.

Now, let us take a quick look at the main HTML mark related code, as follows:

<asp:ScriptManager ID="ScriptManager1" runat="server">

</asp:ScriptManager>

<asp:ListBox ID="ResourceNames" runat="server" Height="196px" Width="332px">

</asp:ListBox>

<p>

<asp:Button ID="btnShow" runat="server" ForeColor="#003399" Height="34px"

Text="Show the image in Ajax Way" Width="187px" onclick="btnShow_Click" />

</p>

<p>

&nbsp;</p>

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">

<ContentTemplate>

<asp:Image ID="Image1" runat="server" />

</ContentTemplate>

<Triggers>

<asp:AsyncPostBackTrigger ControlID="btnShow" EventName="Click" />

</Triggers>

</asp:UpdatePanel>

Since herein only very elementary ASP.NET AJAX techniques are used, I do not plan to give detailed explanations; I will save space for more interesting things.

Now, let us shift our attention to see what happens with the code-behind file AccessDll.aspx.cs. The following gives all of the code in it:

public partial class AccessDll : System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

if (!IsPostBack)

{

Assembly dll = Assembly.LoadWithPartialName("MyImageDll");

string[] resources = dll.GetManifestResourceNames();

ResourceNames.DataSource = resources;

ResourceNames.DataBind();

}

}


protected void btnShow_Click(object sender, EventArgs e)

{


this.Image1.ImageUrl = "GetBmpFromAssembleHandler.ashx?url=" + ResourceNames.SelectedValue;

}

}

Herein, there are two points worth noting. Inside the Page_Load function, we first created an instance of our custom assembly MyImageDll, then by invoking the GetManifestResourceNames function we obtained all the resource names contained within this assembly, and at last specified the name string array to be bound to the ListBox control.

Second, at any time when the user clicks the btnShow button (with text property being "Show the image in Ajax Way" mentioned above), the ImageUrl property of the ASP.NET server control Image is changed accordingly, with the newly-selected item from ListBox control as the new parameter passed the custom HTTP handler GetBmpFromAssembleHandler.ashx.

As we have said, the browser does not mind what kind of string the ImageUrl property specified. What it really cares about is whether the proper Content-type header is supplied and therefore the corresponding image bit stream is reached. In contrast to the first sample application above, this sample introduced another kind of simplified HTTP handler-an .ashx file, which has been registered internally into the ASP.NET configure file-web.config.

Now, let's take some time to do some research on the custom HTTP handler. The following lists all the source code for the GetBmpFromAssembleHandler.ashx file.

public class GetBmpFromAssembleHandler : IHttpHandler {


public void ProcessRequest(HttpContext context)

{

string resourceName = context.Request["url"];


if (resourceName != null)

{

System.Drawing.Image img = LoadImageFromResources(resourceName);

if (img != null)

{

MemoryStream ms = new MemoryStream();

img.Save(ms, ImageFormat.Jpeg );

img.Dispose();


context.Response.ContentType = "Image/jpeg";

context.Response.BinaryWrite(ms.GetBuffer());

ms.Close();

context.Response.End();

}

}

}

public bool IsReusable {

get {

return false;

}

}

private System.Drawing.Image LoadImageFromResources(string imageID)

{

Assembly dll = Assembly.LoadWithPartialName("MyImageDll");

Bitmap img = new Bitmap(dll.GetManifestResourceStream(imageID));

return img;

}


}

The implementation logic here is simple. First, obtain the passed parameter URL and judge whether it is null or not. Second, with the help of the GDI+ Image class and a helper function named LoadImageFromResources(), we grab the interesting image files out of the assembly and store them in an Image object. Third, when the above preparation is completed, an instance of the MemoryStream class is created; the image data in the above Image object are shifted into it (memory) in JPEG form. Finally, after specifying the ContentType property of HttpResponse to 'Image/jpeg' (matching the one specified above), the binary image data is written to the out stream of HttpResponse. Since here we only illustrated a simple HTTP handler, we did not put into use the try-throw-catch blocks.

Finally, let us say a few words about the MyImageDll assembly. In fact, this assembly is very simple since it only contains several picture files. Moreover, the creation process is easy, too.

To create the assembly, click 'File->Add New Project...' and from the left side (project types) select 'Visual C#-->Windows' and from the right side (templates) select 'Class Library.' Name the project MyImageDll. After you press 'OK' and exit the dialog, you will get a Class1.cs file with an empty Class1 class. Just keep it that way. Finally, create a sub folder named imgs and add several image files under it. At last, you can right click the project and select 'Build' to create an assembly named MyImageDll.dll.

To refer to the assembly is easy, too. Right above the web page project named AspnetGdiplus, select 'Add References...'. In the 'Add Reference' dialog box, click the 'Browse' tab and navigate to the MyImageDll.dll assembly. Press OK. That is all.

Next, let us discuss how to deal with image data contained inside a database under the ASP.NET scenario.

Accessing Images from a SQL Server Database

As is indicated above, images in web applications can also be persisted in backend databases. However, there are drastic disputes about storing image data in databases. Anyway, let's first see a sample that shows how to use Microsoft SQL Server databases to access image data.

First, we will construct an example to grab images from a SQL Server database and render them on the web page.

Right click the above sample project and add an AJAX web form named DatabaseMode.aspx. In this application, the newest SQL Server sample database AdventureWorks_Data.mdf is required, in which we will work with the table ProductPhoto that provides two image fields (ThumbNailPhoto and LargePhoto, both of type varbinary) for us to access. The following Figure 3 shows one of the running-time snapshots. When the user selects a PhotoId from the ListBox at the upper left corner and clicks the 'View Product Picture' button, the corresponding product thumbnail will be rendered on the right side in the AJAX way.

Figure 3-the running-time snapshot for sample 3


Now, you can gradually find out the key how-tos, as follows. First, let's look at the page-related HTML elements design:

<form id="form1" runat="server">

<div>

<asp:ScriptManager ID="ScriptManager1" runat="server" />

</div>

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">

<ContentTemplate>

<table><tr>

<td valign="top">

<asp:Label ID="Label1" runat="server" Text="Please Select Product PhotoId:"></asp:Label>

<br />

<asp:DropDownList ID="prductList" runat="server"

DataSourceID="SqlDataSource1"

DataTextField="ProductPhotoID"

DataValueField="ProductPhotoID" />

&nbsp;&nbsp;&nbsp;

<asp:Button ID="Button1" runat="server" OnClick="Button1_Click"

Text="View Product Picture" BorderColor="#3333FF"

/>

</td>

<td valign="top">

<asp:Image ID="Image1" runat="server" Height="160px" Width="307px" />

 

</td>

</tr>

</table>

</ContentTemplate>

<Triggers>

<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />

</Triggers>

</asp:UpdatePanel>

<asp:SqlDataSource ID="SqlDataSource1" runat="server"

ConnectionString="<%$ ConnectionStrings:myAdventureWorks %>"

SelectCommand="SELECT [ProductPhotoID] FROM Production.ProductPhoto" >

</asp:SqlDataSource>

</form>

In this AJAX page, we used an ASP.NET AJAX server side control named UpdatePanel to enclose the <table/>, inside of which lie the ListBox control and an Image control for rendering images. Moreover, as a typical ASP.NET implementation, we introduced the SqlDataSource component to facilitate setting up bridges between the presentation tier and the backend storage.

Note in the SelectCommand property of the SqlDataSource control we have not 'Selected' the ThumbNailPhoto field and attached it to the Image control. In fact, we would achieve nothing (no images appear on the page) even if we did. Therefore, to render images in this database-related solution, we will have to fall back on another technique-the HTTP handler. The following gives the usage of the handler in this sample:

protected void Button1_Click(object sender, EventArgs e)

{

string url = String.Format("DatabaseImageHandler.ashx?PhotoID={0}", prductList.SelectedValue);

//string url = String.Format("ImageHandler.axd?PhotoID={0}", prductList.SelectedValue); //mentioned later

Image1.ImageUrl = url;

}

So when the user clicks the 'View Product Picture' button the DatabaseImageHandler.ashx is invoked to transform and transport data to the  ImageUrl property of the Image1 control.

To write the above handler, right click the project and add a 'Generic Handler' named DatabaseImageHandler.ashx. As for the .ashx HTTP handler, since ASP.NET has provided a built-in item to the IIS meta database, we do not need to add a new <httphandler> within the web.config file and encapsulate the handler into an independent assembly. So now things are simple for us; we just need to write the .ashx HTTP handler, as follows:

public class DatabaseImageHandler : IHttpHandler {

public void ProcessRequest (HttpContext context) {

// Ensure the URL contains an ID argument being a number

int id = -1;

bool result = Int32.TryParse(context.Request.QueryString["PhotoID"], out id);

if (!result)

{

context.Response.End();

}


string connString = ConfigurationManager.ConnectionStrings["myAdventureWorks"].ConnectionString;

string cmdText = "SELECT ThumbNailPhoto FROM Production.[ProductPhoto] WHERE ProductPhotoID=@PhotoID";


// Get an array of bytes from the BLOB field

byte[] img = null;

SqlConnection conn = new SqlConnection(connString);

using (conn)

{

SqlCommand cmd = new SqlCommand(cmdText, conn);

cmd.Parameters.AddWithValue("@PhotoID", id);

conn.Open();

 

SqlDataReader reader = cmd.ExecuteReader();

reader.Read();

img = (byte[])reader[0];

conn.Close();

}

// Prepare the response for the browser

if (img != null)

{

context.Response.ContentType = "image/jpeg";

context.Response.BinaryWrite(img);

}

}

 

public bool IsReusable {

get {

return false;

}

}

}

This HTTP handler is very much like the one in the previous sample, except the ADO.NET related components work. Here we specify the ContentType property of HttpResponse as "image/jpeg" and then use the BinaryWrite() method to write the image data to the output stream.

Please note, for integrity, I've also provided the more generic way to write the HTTP handler. Below I've listed several points to notice:

1) Add a new project at the web site named DatabaseHTTPHandler, and in the default file Class1.cs write the above HTTP handler (you will need to add related references to the required system assemblies).


2) Build the DatabaseHTTPHandler project, and you will get an assembly named DatabaseHTTPHandler.dll.


3) Add the reference to the DatabaseHTTPHandler.dll assembly to the above web site project AspnetGdiplus.


4) Register the related information in the web.config file, as follows:

<httpHandlers>

......

<add verb="*" path="ImageHandler.axd" type="DatabaseHTTPHandler.ImageHandler, DatabaseHTTPHandler" validate="false"/>

</httpHandlers>

5) Use the custom HTTP handler, as shown in the above-mentioned  Button1_Click function (the commented line).

For all the details, please see the attached source code with this article.

For now, we've only tackled half of the problem-accessing image data in database. The other half we've not looked at so far is how to store image data in a database. In fact, I have already given the answer for this half in my article (one of a long series) ' Back-end Management Tasks for an ASP.NET AJAX Server-Centric Based Online Shopping Website '.

Now that you know the key for the second half, let's go on with the story-dynamically generating images using GDI+ and more.

Author's Note: Whether to persist image data in databases depends on many concrete conditions, such as the size of a single image file, the number of image files, the kind of database, the memory volume of the backend, and whether to leverage the ASP.NET buffering mechanism. In practical scenarios, sometimes you can store image files under some sub folders in the website. Sometimes if the sizes and number of image files meet specified conditions, you can also take into account the buffering technique. In Dino Esposito's opinion, if you frequently edit the images, you'd better persist the image files in an independent file form. If the image files are huge (tens or hundreds of mega-bytes) you are also advised to store them in the file system form, while if the images are mainly read-only and static as well as small (less than thousands of bytes), the database solution will be your best choice.

-DOWNLOAD SOURCE PART 1-

blog comments powered by Disqus
ASP.NET ARTICLES

- Implementing ASP.NET 4.0 Page.MetaDescriptio...
- ASP.Net Development Tips
- Intro to Sessions in ASP.Net
- Google Maps API Introduction in ASP.NET usin...
- Creating an ASP.NET 3.5 Gridview Image Galle...
- Encrypt QueryString in ASP.NET 3.5 using VB....
- ASP.NET 3.5 Drop Down List Controls
- Connect to Access Database with ASP.Net
- Secure Audio Streaming with ASP.Net and Flash
- Dynamic Sitemap and Navigation in ASP.Net
- Implement Gzip and Deflate Compression in AS...
- Run ASP.Net in Ubuntu with Apache
- ASP.Net Mono Website Contact Forms
- ASP.Net URL Rewriting Methods
- Murach`s ASP.NET 4 Web Programming with C# 2...

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 6 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials