Explanation:
We already included some Articles about displaying Image in ASP .NET. It was about displaying and saving an Image in MS SQL Database.
Saving an Image to Profile:
ALL data types which are Serealizable can be saved in the Profile. Serialization is the process of saving the state of an object by converting it to a stream of bytes. The object can then be persisted to file, database, or even memory. The reverse process of serialization is known as deserialization . (see the following Figure)
Uses of serialization
Serialization is used in many scenarios, but the main purpose is to save the state of an object in order to have the ability to recreate the same object when required. It is an important to let the user save work and then be able to continue from that point at a later time. This is a common need in various tools and applications. Serialization is also used in creating a clone of an object.
Another important need for serialization arises when the object is required to travel electronically over wire. In such cases the objects are serialized and deserialized. In fact, serialization is one of the fundamental requirements for techniques such as .NET Remoting.
Even the hibernation mode in the Windows Operating system can be considered a form of serialization.
Image serialization
S erialization is an important entity of pipelining an Object from one point to other point. The .NET Framework provides certain built-in mechanisms which can be used to serialize and deserialize objects. This functionality can be found in the System.Runtime.Serialization. and the System.Xml.Serialization namespaces of the .NET Framework
Here we will restrict our discussion to serialization of an Image. Image is serialized as a binary data to the Database or a file.
In ASP .NET to show an Image from a location would be an easy target as we can use simply “src” property of an Image control with pointing it to the image file location and every thing will be fine. But harder efforts need to perform when we want to show an image from Database.
Saving an image to a database is a different story also. It needs different programming technique to store a binary data (Image) into a database rather then saving it (Image) to a file location.
After long theoretical Introduction now we are able to describe the methods to save/retrieve an Image to/from a Profile (as Profile is also saving data to a Database).
The 1st step will be to tell the Web server/Database server that you are adding a new field to the User Profile system. We will perform this like the following:
<profileenabled="true"automaticSaveEnabled="false" >
<properties>
-----other properties come here------
<addname="Picture"
type="System.Drawing.Bitmap"
serializeAs="Binary"/>
properties>
profile>
You noticed that the Data type is a “Bitmap”. Why not an Image? Image is an Abstract class which is meant to Inherit. This is a base class that provides functionality for the Bitmap and Metafile descended classes.
Abstract classes are like Interfaces with the difference that methods and properties of an abstract Class can contain different types of Accessor where as an Interface does not contain any type of Accessor i.e. it is solely created to implement.
There are some similarities and differences between an interface and an abstract class that I have arranged in a table for easier comparison:
Feature
|
Interface
|
Abstract class
|
Multiple inheritance
|
A class may inherit several interfaces.
|
A class may inherit only one abstract class.
|
Default implementation
|
An interface cannot provide any code, just the signature.
|
An abstract class can provide complete, default code and/or just the details that have to be overridden.
|
Constants
|
Only Static final constants.
|
Both instance and static constants are possible.
|
Core VS Peripheral
|
Interfaces are used to define the peripheral abilities of a class. In other words both Human and Vehicle can inherit from a IMovable interface.
|
An abstract class defines the core identity of a class and there it is used for objects of the same type.
|
Homogeneity
|
If the various implementations only share method signatures then it is better to use Interface.
|
If the various implementations are of the same kind and use common behaviour or status then abstract class is better to use.
|
Speed
|
Requires more time to find the actual method in the corresponding classes.
|
Fast
|
Adding functionality
|
If we add a new method to an Interface then we have to track down all the implementations of the interface and define implementation for the new method.
|
If we add a new method to an abstract class then we have the option of providing default implementation and therefore all the existing code might work properly.
|
To save the Image in Profile would be more easy assignment then retrieving from the Profile. See the following code for saving an Image:
ProfileCommon prof = Profile.GetProfile("UserName");
prof.Picture = new System.Drawing.Bitmap(
Server.MapPath(@"ImageFolder\Logo-Stars.png"));
prof.Save();
We retrieved the user profile, created a new bitmap and then assigned it to the Profile Field.
To Retrieve the Image we need to cerate a Binary stream. For this we will use System.IO Namespace.
Now, the hard story starts. As we said that we will load an image using a stream. Normally, we can do this by cerating a Method which return type is a stream. In ASP .NET its not easy to display image as tage loads an Image using src attribute. This only allows a file or a web page path to load an Image. So what is the solution? Yes there is a solution. In ASP .NET we use a handler for this reason.
What is a Handler?
A handler is responsible for fulfilling requests from a browser. Requests that a browser manages are either handled by file extension (or lack thereof) or by calling the handler directly. Only one handler can be called per request. A handler does not have any HTML static text like .aspx or .ascx files. A handler is a class that implements the IHttpHandler interface. If you need to interact with any Session information, you will also need to implement IRequiresSessionState. If you want to make an asynchronus handler, you will need to implement the IHttpAsyncHandler interface instead of the IHttpHandler interface.
Calling a Handler by File Extension
A handler can be invoked by any file extension that is mapped via the web.config and the IIS file extension mappings. You can have a file named something.billybob where 'billybob' is the file extension, or you can have no file extension at all such as http://web/handler/file where a request for 'file' without an extension invokes a handler mapped to *.* or the directory as *.
Calling a Handler Directory
The code file for the handler has the file extension of 'ashx' on the web server. This file can be called directly via a browser without having to set up web.config or IIS file extension mappings. For example: http://web/handler/handler.ashx.
Some examples of this hander type are photo albums, RSS feeds, and blogging sites. Each of these is a good example of work that can be better accomplished without standard HTML. A photo album involves directory crawling and responding with pictures. A RSS feed returns information in the correct format.
If you cerate a Handler with visual studio it will look like this:
<%@ WebHandler Language="C#" Class="Handler" %>
using System;
using System.Web;
public class Handler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
context.Response.Write("Hello World");
}
public bool IsReusable {
get {
return false;
}
}
}
By browsing this Handler we will get a Text "Hello World" in our page. If you noticed that we always have to copy our data to the Context. Data can wrote to context either as a Text or as a Binary data.
When we want to copy a simple text to the Context we use Response.Write() method but how to Copy a Binary data? As we discribed earlier Image is a binary data (in GDI Langauge it’s a Two dimesional Array of Pixels. Don’t mix these two defination as both are ture i.e. Two dimensional data is saved in binary Format). We use Response.OutputStream.Write () to write a Binary data to the Context.
There is a little different senario in Plan C# Application (GDI, GDI+) that we use the Bitmap.Save() method instead for saving the Binary data to out put stream. So don’t get confuse in here because in Web page development our stream needs to Serialized to the Client Context that’s why we use Response.OutputStream.Write ().
I guess this was kind of simple to undertsand the difference. Now the question comes which Overload method we can use to write an Image to the Outputstream of the Context? We will use the method which takes Byt array of the Binary Data. Now see how dows our code look like this:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/jpeg";
context.Response.Cache.SetCacheability(HttpCacheability.Public);
ProfileCommon prof = new ProfileCommon();
ProfileCommon prof2 = prof.GetProfile(context.Request.QueryString["UserName"]);
Bitmap myBitmap = prof2.Picture;
byte[] b = GetImageBytes(myBitmap);
context.Response.OutputStream.Write(b, 0, b.Length);
}
We created a Profile Object then used its method to fill the other object, with the User Profile Data. We can not do this in one line because we need a Profile Object before we can use its method GetProfile(). As in Handler Context we can not access the Page Methods. Where context.Request.QueryString["UserName"] is used to access the Qureystring member “UserName” (we will account this later when we pass a member to the Handler).
What if we want to access an image from a File location? In this senario we need a Page Object as it contains the Server.Map() method. This will look like the following:
Page pg = new Page();
Bitmap myBitmap = new Bitmap(pg.Server.MapPath(@"ImageFolder\Logo-Stars.png"));
Now see how to convert the Bitmap to a byte Array.
private byte[] GetImageBytes(Image image)
{
ImageCodecInfo codec = null;
foreach (ImageCodecInfo e in ImageCodecInfo.GetImageEncoders())
{
if (e.MimeType == "image/png")<
{
codec = e;
break;
}
}
using (EncoderParameters ep = new EncoderParameters())
{
ep.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, codec, ep);
return ms.ToArray();
}
}
}
This is actually the main trick. If you try to save the Image stream in a Stream format to the outputstream it will simply not work because Data is Transferred/Transmited in the Bytes format. So we have to convert the Stream in Bytes first before sending it int to the Pipeline.
Now the last point comes how to assign the Handler to a Tage? Its very simple:
<img runat="server" id="image" src="ProfilePicHandler.ashx?UserName=SomeName" alt="Profile Image" />
Here we are passing a Qureystring member “UserName” to the Handler. Values after “?” come in the account of Querystring and it can be accessed at the Distination Page. Here Mamber Name is “UserName” and its value is “SomeName”.
If you are planing to make a Dynamic generation users profile images then you can use code behind instead. But, you have to get the UserName from any source. The source could be a Membership and MembershipUser Objects.
public void uploadUserPictures()
{
MembershipUserCollection users = Membership.GetAllUsers();
foreach (MembershipUser user in users)
{
image.sr = "ProfilePicHandler.ashx?UserName="+user.UserName;
}
}
So our whole code of the Handler will look like this:
<%@ WebHandler Language="C#" Class="ProfilePicHandler" %>
using System;
using System.Web;
using System.Web.Profile;
using System.Web.Security;
using System.IO;
using System.Web.UI;
using System.Drawing;
using System.Drawing.Imaging;
public class ProfilepicHandler : IHttpHandler {
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/jpeg";
context.Response.Cache.SetCacheability(HttpCacheability.Public);
ProfileCommon prof = new ProfileCommon();
ProfileCommon prof2
= prof.GetProfile(context.Request.QueryString["UserName"]);
Bitmap myBitmap = prof2.Picture;
byte[] b = GetImageBytes(myBitmap);
context.Response.OutputStream.Write(b, 0, b.Length);
}
public bool IsReusable {
get {
return false;
}
}
private byte[] GetImageBytes(Image image)
{
ImageCodecInfo codec = null;
foreach (ImageCodecInfo e in ImageCodecInfo.GetImageEncoders())
{
if (e.MimeType == "image/png")
{
codec = e;
break;
}
}
using (EncoderParameters ep = new EncoderParameters())
{
ep.Param[0] = new EncoderParameter(Encoder.Quality, 100L);
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, codec, ep);
return ms.ToArray();
}
}
}
}
Hope it Help.
|