Upload and save image in a database - ExtJs and ASP.NET

It's been a while since the last post appeared here so this one will be rather longish ;-)

Recently in one of our projects we had a requirement to allow users to upload their images to the database so they can later be viewed by other users of the application.

For the clientside we used the ExtJs framework while the serverside was ASP.NET and c#.

Basically the process can be divided into three parts: image upload on the clientside, image processing on the server side and also image retrieval from the database so it can be displayed again in a browser.

So lets start with the first part. Below is an example of an ExtJs upload window followed by the code:

 

Ext.define('myApp.imageUploadFormUi', {
    extend: 'Ext.window.Window',
    title: 'Image upload',
    width: 410,
    layout: 'anchor',
    buttonAlign: 'center',
    autoHeight: true,
    closable: false,
    modal: true,
    
    initComponent: function(){
    	this.items = [
	    	new Ext.form.FormPanel({
               fileUpload: true,
               layout: 'fit',
               frame: true,
               autoHeight: true,
               bodyStyle: 'padding: 10px 10px 0 10px;',
               
               items: [
               {
                   xtype: 'fileuploadfield',
                   id: 'form-file',
                   emptyText: 'Select your image',
                   hideLabel: true,
                   name: 'uploadfile'
               },
			   {
                   xtype: 'textfield',
                   name: 'oid',
                   hidden: true
               }]
    		})
    	];
	 	
	 	this.fbar = {
            xtype: 'toolbar',
            items: [
            	{
                    xtype: 'button',
                    text: 'Ok',
                    action: 'login',
                    id: ''
                },
                {
                    xtype: 'button',
                    text: 'Cancel',
                    action: 'cancel',
                    id: ''
                }
            ]
        };
	 	this.callParent(arguments);
    }
});

Ext.define('myApp.imageUploadForm', {
	extend: 'myApp.imageUploadFormUi',
    
	initComponent: function(){
		
		this.callParent(arguments);
		
		//get controls first - an imitation of v3 autoref
		this.ctrl_btnOk = this.query('button[action=login]')[0];
		this.ctrl_btnCancel = this.query('button[action=cancel]')[0];
		
		this.ctrl_oidField = this.query('textfield[name=oid]')[0];
		this.ctrl_oidField.setValue('Here I get an id of a user that is about to upload his image');
		
		//wire up events
        this.ctrl_btnOk.on('click', this.onOkBtnClick, this);
		this.ctrl_btnCancel.on('click', this.onCancelBtnClick, this);
	},
    
	onCancelBtnClick: function(){
		this.close();
	},
	
	onOkBtnClick: function(){
		
		this.showLoadMask();
		
		var form = this.items.items[0].getForm();
		
		if(form.isValid){
			form.submit({
           	url: 'imageUpload.aspx',
	           success: Ext.bind(
	           		function (form, action) {
					this.hideLoadMask();
           			this.close();
               	}, this),
               
               	failure: Ext.bind(function (form, action) {
           			this.hideLoadMask();
                   	Ext.Msg.alert('info', action.result.uploadFeedback);
               	}, this)
           });
		}
	},
	
	//load mask
    loadMask: null,
    showLoadMask: function(){
    	if(this.loadMask == null){
    		this.loadMask = new Ext.LoadMask(this.getEl(), { msg: 'Uploading your picture...' });
    	}
    	this.loadMask.show();
    },
    hideLoadMask: function(){
    	this.loadMask.hide();
    }
});

 The selected image is sent to the imageUpload.aspx for further processing. While the image is being processed a loading mask appears over the window and whenever the server returns some feedback the upload window either closes itself or shows an error message. Along with the image there is also a user id sent to the server so the code knows where to store the data.

Once we have the data sent to the server side we can access the sent image in order to store it in a database. The aspx web form we are using has two parts - the actual aspx file and the code file. For the aspx file it is enough to just reference the code file:

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

The actual code file is a bit longer. All the data is processed in the Page_Load method and there is another method for image resizing:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using System.IO;
using System.Text;

public partial class imageUpload : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

        HttpContext context = HttpContext.Current;

        bool success = false;
        string uploadFeedback;

        int oid = -1;
        Int32.TryParse(Request["oid"], out oid);


        if (context.Request.Files.Count > 0)
        {
            string[] supportedTypes = new string[] { "png", "gif", "jpg", "jpeg" };

            string postedFileName = context.Request.Files[0].FileName;
            string x = Path.GetExtension(postedFileName);
            if (supportedTypes.Contains(x.TrimStart('.')))
            {

                //rescale the image to 150x150
                System.Drawing.Image image = resizeImage(System.Drawing.Image.FromStream(context.Request.Files[0].InputStream));


                //database & table
                string dbName = System.Configuration.ConfigurationSettings.AppSettings["db"];
                string tblPinColor = System.Configuration.ConfigurationSettings.AppSettings["tblPinColor"];



                //Conection string
                string ConnectionString =
                    "server=" + System.Configuration.ConfigurationSettings.AppSettings["server"] + ";" +
                    "user id=" + System.Configuration.ConfigurationSettings.AppSettings["userName"] + ";" +
                    "password=" + System.Configuration.ConfigurationSettings.AppSettings["userPswd"] + ";" +
                    "database=" + dbName + ";";


                //sql server connection object
                System.Data.SqlClient.SqlConnection sqlServerConnection = new System.Data.SqlClient.SqlConnection(ConnectionString);

                //create select command
                System.Data.SqlClient.SqlCommand sqlCommand = new System.Data.SqlClient.SqlCommand("", sqlServerConnection);


                string insertImage =
                    "UPDATE [" + dbName + "].[dbo].[" + tblPinColor + "] " +
                    "SET [PC_04] = @image " +
                    "WHERE [user_id] = @userId;";

                sqlCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@userId", System.Data.SqlDbType.Int)).Value = oid;


                // Convert image to memory stream
                System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
                image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);

                sqlCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@image", System.Data.SqlDbType.Image)).Value = memoryStream.ToArray();


                //connect to the server
                System.Data.SqlClient.SqlTransaction transaction = null;//transaction object
                try
                {
                    //open connection
                    sqlServerConnection.Open();

                    //begin transaction
                    transaction = sqlServerConnection.BeginTransaction();

                    //assign transaction to a command
                    sqlCommand.Transaction = transaction;


                    //tblBasic
                    sqlCommand.CommandText = insertImage;
                    sqlCommand.ExecuteNonQuery();

                    transaction.Commit();

                    success = true;
                    uploadFeedback = "Your image has been uploaded!";

                }
                catch (Exception ex)
                {
                    transaction.Rollback();

                    //log the error so it can be reviewed
                    mismap.ErrorLogging.logError(System.Reflection.MethodBase.GetCurrentMethod().Name, ex.Message);

                    success = false;
                    uploadFeedback = "Image upload failed.";
                }
                finally
                {
                    //close the connection
                    sqlServerConnection.Close();
                }

            }
            else
            {
                // document not supported!
                success = false;
                uploadFeedback = "File type not supported.";
            }
        }
        else
        {
            uploadFeedback = "Image upload failed.";
        }

        string successStr;
        if (success)
        {
            successStr = "true";
        }
        else
        {
            successStr = "false";
        }

        Response.ContentType = "text/html";
        Response.Write("{\'uploadFeedback\':\'" + uploadFeedback + "\',\'success\':" + successStr + "}");

    }

    private System.Drawing.Image resizeImage(System.Drawing.Image inputImage)
    {

        int maxWidth = 135;
        int maxHeight = 135;

        // Prevent using images internal thumbnail
        inputImage.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);
        inputImage.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);

        int newWidth = inputImage.Width;
        if (inputImage.Width >= maxWidth)
        {
            newWidth = maxWidth;
        }

        int newHeight = (int)(((double)inputImage.Height / (double)inputImage.Width) * newWidth);
        if (newHeight > maxHeight)
        {
            //new height still too much; resize with height instead
            newHeight = maxHeight;
            newWidth = (int)(((double)inputImage.Width / (double)inputImage.Height) * maxHeight);
            
        }

        System.Drawing.Image newImage = inputImage.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero);

        //got the image so now paint it over a blaank image so it is nicely centered
        System.Drawing.Bitmap baseImage = new System.Drawing.Bitmap(maxWidth, maxHeight);
        using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(baseImage))
        {
            int x = (int)((double)((maxWidth - newImage.Width)) / 2);
            int y = (int)((double)((maxHeight - newImage.Height)) / 2);

            g.DrawImage(
               newImage, //source image
               new System.Drawing.Rectangle(x, y, newImage.Width, newImage.Height) //destination rectangle
            );
        }

        return baseImage;
        //return newImage;
    }
}

Once we have an image stored in a databes we have to find a way of retrieving it and passing back to the clientside. To do so we have to simply grab the data from the database and send it back.

The code retrieving the image has to know what is the id of the user it is about to retrieve the data for. If the id is invalid or there is no data in the database our getImage.aspx web form returns a default no-face image. We pass the id as a simple param ?oid=696.

Again we are using an aspx web form and it has two files:

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

 The code part of the getImage is as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class getImage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

        //database & table
        string dbName = System.Configuration.ConfigurationSettings.AppSettings["db"];
        string tblPinColor = System.Configuration.ConfigurationSettings.AppSettings["tblPinColor"];

        int oid = -1;
        Int32.TryParse(Request["oid"], out oid);

        //Conection string
        string ConnectionString =
            "server=" + System.Configuration.ConfigurationSettings.AppSettings["server"] + ";" +
            "user id=" + System.Configuration.ConfigurationSettings.AppSettings["userName"] + ";" +
            "password=" + System.Configuration.ConfigurationSettings.AppSettings["userPswd"] + ";" +
            "database=" + dbName + ";";


        //sql server connection object
        System.Data.SqlClient.SqlConnection sqlServerConnection = new System.Data.SqlClient.SqlConnection(ConnectionString);

        //create select command
        System.Data.SqlClient.SqlCommand sqlCommand = new System.Data.SqlClient.SqlCommand("", sqlServerConnection);

        string pullImage =
            "SELECT " +
                "[PC_04] " +
            "FROM [" + dbName + "].[dbo].[" + tblPinColor + "] " +
            
            "WHERE [user_id] = @userId;";

        sqlCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@userId", System.Data.SqlDbType.Int)).Value = oid;

        try
        {
            //open connection
            sqlServerConnection.Open();

            //tblBasic
            sqlCommand.CommandText = pullImage;
            object imageData = sqlCommand.ExecuteScalar();
            
            if (imageData is DBNull || imageData == null)
            {
                System.Drawing.Image blankImage = System.Drawing.Image.FromFile(Server.MapPath(System.Configuration.ConfigurationSettings.AppSettings["blankProfileImg"]));
                System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();
                blankImage.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);

                blankImage.Dispose();

                Response.ContentType = "image/jpeg";
                Response.BinaryWrite(memoryStream.ToArray());
            }
            else
            {
                byte[] image = (byte[])imageData;

                Response.ContentType = "image/jpeg";
                Response.BinaryWrite(image);
            }
        }
        catch (Exception ex)
        {
            //log the error so it can be reviewed
            mismap.ErrorLogging.logError(System.Reflection.MethodBase.GetCurrentMethod().Name, ex.Message);
        }
        finally
        {
            //close the connection
            sqlServerConnection.Close();
        }
    }
}

And this is about it. Please bear in mind that the code presented is just an example and may not suit your needs in 100%. It may also need some further error catching. If you want to avoid the overhead caused by the events fired by the loading web form, you may consider using a webhandler (ashx) instead.

blog comments powered by Disqus