Images and working with a DAL
Most examples of working with saving images to a SQL Server database within a application that does not implement a DAL, usually work off only a HTTPPosted file and looks kinda like this:
//create a stream
Stream imgStream = proposedFile.InputStream;
//get the length of the http posted file
int imgLen = proposedFile.ContentLength;
//now loop through the stream and assign it to the byte[] (which is an array)
byte[] imgBinaryData = new byte[imgLen];
int n = imgStream.Read(imgBinaryData, 0, imgLen);
//now do the SQL portion
SqlConnection conPortal = new SqlConnection(CommunityGlobals.ConnectionString);
SqlCommand cmdAdd = new SqlCommand(spToUse, conPortal);
cmdAdd.CommandType = CommandType.StoredProcedure;
cmdAdd.Parameters.Add("@RETURN_VALUE", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;
cmdAdd.Parameters.Add("@communityID", CommunityGlobals.CommunityID);
cmdAdd.Parameters.Add("@imageID", imageID);
cmdAdd.Parameters.Add("@imageName", imgFileName);
cmdAdd.Parameters.Add("@contentType", imgContentType);
cmdAdd.Parameters.Add("@imageData", SqlDbType.Image).Value = imgBinaryData;
conPortal.Open();
cmdAdd.ExecuteNonQuery();
conPortal.Close();
This process works well without the DAL abstraction because everything is all contained in a single method. When one tries to implement this using a DAL there is a latency disconnect and at times (sporadic) , the bytes[] may (I shall emphasize MAY) get truncated. Interestingly enough, during my testing of a DAL implementation, each subsequent request for saving the object resulted in a longer byte length transmitted...
My real issue however, was doing some performance improvements on my image handler. Originally, I was retrieving a full size image and re-sizing it in memory - to a defined thumbnail image size for display. While, I was implementing caching - the initial rendering was simply slow. Additionally, the thumbnails are used for lists or menus and I also wanted the ability to modify the thumbnails so that met certain defined requirements and may not be direct representations of the original posted file. Perhaps all done in sepia tone, image would be cropped, or water marks would be applied for example.
If there is one thing - that we must be careful of when manipulating images - is any manipulation decreases the quality of the image. So, my performance goal at sake of database storage (there is always a trade-off) - was a new table - that stored the thumbnails. My ImageHandler checks for thumbnails in the database, if one doesn't exist then it grabs the original image - and performs the resize - and saves it to the database.
What made the issue more complex was that I am enforcing the use of my custom DAL, which for the afore-mentioned reasons,the issues of latency needed to be addressed as well..The previous example merely illustrates how the code looked prior to doing the whole n-tier deal.
Here is the revised code:
public void SaveImage(int imageID, string ContentType ,string ImageName,string CMDTEXT,Image image, int SID)
{
// Update the image in the database
MemoryStream imgStream = new MemoryStream();
//ContentType is a string passed into the method. GetImageFormat converts the string to the
ImageFormat enumerable required. image is type of Image.
image.Save(imgStream, GetImageFormat(ContentType));
//Instead of doing the whole calculate stream length (which is a heck of alot easier to do with
//HttpPostedFile than a Image type... we call GetBuffer() - this fills the byte[] variable with
//the image bytes[] (basically it is all now an array.... )
byte[] imgBinaryData = imgStream.GetBuffer();
//the rest here is based on previous blog posts and is how I handle my DAL and pass
//parameters...
List<ParmInfo2> PARMS_TOPASS = new List<ParmInfo2>();
PARMS_TOPASS.Add(new ParmInfo2("images", "CID", CommunityGlobals.CommunityID));
PARMS_TOPASS.Add(new ParmInfo2("images", "CPID", imageID));
PARMS_TOPASS.Add(new ParmInfo2("images", "SID", SID));
PARMS_TOPASS.Add(new ParmInfo2("images", "imagename", ImageName));
PARMS_TOPASS.Add(new ParmInfo2("images", "CType", ContentType));
PARMS_TOPASS.Add(new ParmInfo2("images", "Data", imgBinaryData));
dalGlobals.ExecuteNonQuery(CommandType.StoredProcedure, CommunityGlobals.ConnectionString, CMDTEXT, PARMS_TOPASS);
//TIP: ALWAYS dispose of the Image or MemoryStream when used in a method...
imgStream.Dispose();
image.Dispose();
The DAL now properly handles the saving of images, with the use of the GetBuffer(). Additionally, it also saves three lines of code (not that that is an important reason - but less code is well less code...)..The above code also demonstrates how to save images when the image is based off a an existing image imported from the file system or a data store with a DAL in place.