Monday, October 28, 2013

Uploading/saving image file content to DB with resizing (EF Code First, SQLServer 2008 R2, MVC4)

File uploading is a very common task in nowadays specially with web applications. Normally what we do is we save the uploading files in the server-side. Recently, I have developed a software component which can be used to save images directly to a database table field(even with re-sizing if needed). For this module, I have used Entity Framework, Sql Server 2008 R2 and MVC4. How it has been achieved is described below.

Table - Content


Content table contains two fields FileName, FileContent and FIleType, which are nvarchar(64), varbinary(MAX) and nvarchar(64) respectively. The file contain was stored in FileContent field.

Entity Class - Content

Entity Map - ContentMap

Helper Class - ImageHelper 
I have implemented ToByteArray method as an extension method for HttpPostedFileBase. As this described if the image dimension is not meet your desired height or width it will be resized and stored.

Note: If you are hoping to store non-image files in DB, still it can be accomplished using ToByteArray extension method.
   
View

Controller - ContentFileController
I have used the extension method ToByteArray with the width and height parameters which obtained by web.config.

View - Uploaded Files

Output


Monday, August 5, 2013

Parsing XML to Collection of Dynamic Objects (LINQToSQL, dynamic, ExpandoObject, MVC WebGrid)

Recently I have experienced with some of the new features (I guess these components are rarely used) of C# 4.0 such the dynamic type, ExpandoObject. My initial requirement was to build a dynamically created object collection out of XML formatted SQL result. Then it has to be used as a data-source for my grid.



Note: The select statement was dynamically generated and the result were vary from one query to another. My intent was to generate the data-source (list of objects) without using a well-structured mapping-class. With the use of ExpandoObject I was able to generate an object in run-time with dynamic properties.




SQL Query which return a XML formatted result

SELECT TOP 5 OT.Id as Id, O.Name As Name, OT.StartTime As StartTime FROM Call OT
LEFT OUTER JOIN Outlet O ON OT.Outlet_SiteUid=O.SiteUid AND OT.OutletUid=O.Uid
WHERE OT.CreationDate BETWEEN '2011-10-01' AND '2012-10-31'
FOR XML PATH('Result'), ROOT('Results')


XML Formatted result

<Results>
  <Result>
    <Id>CARC00000607</Id>
    <Name>CENTRAL (ZHONG HUAN CHA CAN TING) - 313 @SOMERSET</Name>
    <StartTime>2011-11-28T09:22:28</StartTime>
  </Result>
  <Result>
    <Id>CARC00000608</Id>
    <Name>CENTRAL (ZHONG HUAN CHA CAN TING) - 313 @SOMERSET</Name>
    <StartTime>2011-11-28T10:16:12</StartTime>
  </Result>
</Results>


For ease of explanation purpose I have copied the XML result to a XML file called "SQLResult1.xml".

DynamicXmlConverter helper class

public class DynamicXmlConverter
{
        /// <summary>
             /// http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx
             /// http://msdn.microsoft.com/en-us/library/vstudio/dd264741.aspx
             /// </summary>
             /// <param name="descendantName"></param>
             /// <param name="filePath"></param>
             /// <returns>List<dynamic></returns>

        public static List<dynamic> GenerateDatasource(string descendantName, string filePath)
        {
            //intialize list of dynamic objects
            var dataList = new List<dynamic>();

            //xml-file loading
            var doc = XDocument.Load(filePath);

            //selecting all the Descendats of Ex: "Result"
            var descendants = doc.Descendants(descendantName);

            //loop through each Descendats
            foreach (var descendant in descendants)
            {
                //creating the ExpandoObject
                dynamic expandoObject = new ExpandoObject();

                //casting it to a dictionary object
                var dictionaryExpandoObject = (IDictionary<string, object>)expandoObject;

                //loop through each elements of descendant
                foreach (var element in descendant.Elements())
                {
                    //assigning of element name as propertyName
                    var propertyName = element.Name.LocalName;
                    //adding the property name and value to the dictionary
                    dictionaryExpandoObject.Add(propertyName, element.Value);
                }
                //finally add each ExpandoObject to list
                dataList.Add(dictionaryExpandoObject);
            }
            return dataList;
       }

}

Controller

public class HomeController : Controller
{       
        public ActionResult LoadGrid()
        {
            var dataList = DynamicXmlConverter.GenerateDatasource("Result",    Server.MapPath("SQLResult1.xml"));
            return View(dataList);
        }

}

View

@{
    ViewBag.Title = "Home Page";
}
@model System.Collections.IEnumerable
@{

    var grid = new WebGrid((IEnumerable<dynamic>)Model, canPage: true, rowsPerPage: 5);
}
@grid.GetHtml()



Output


Tuesday, April 30, 2013

Android MapActivity cannot be resolved

While I was working in a Android Map based application, I came across that MapActivty couldn't be resolved. Since I was upgraded my SDK Tools & SDK Platform Tools to the very latest versions, I was unable to set the target of my app to Google API [desired version] as mentioned in many tutorials. Finnaly I was able to fix the issue by manually setting the Android Build Target of my project to Google API(Platform 3.1, API Level 12)

Eclipse -> Select the project -> Properties -> Android -> Target Name -> tick the desired Google API level

Accessing WCF Service via Android

Recent past as an R&D Project I was working on a prototype basis Shopping cart application (assuming that the end-users are always in a connected environment-WiFi/3G) which has no local database so that it had to interact with WCF Service for data-manipulation.

WCF Service, data handling section was developed using Entity Framework(Code First approach). There were several modules like Item, Category, Order etc. Among those I picked Item module for further explanation.

WCF Service

Entity
public class Item{

        public string Id { get; set; }

        public string Name { get; set; }

        public string Url { get; set; }

        public string CategoryId { get; set; }

        public virtual Category Category { get; set; }

        public int InStock { get; set; }

        public decimal Price { get; set; }

 }

Mapping
public class ItemMap : EntityTypeConfiguration<Item>
{
        public ItemMap()
        {
            ToTable("tbl_Item");
            HasKey(item => item.Id);

            HasRequired(item => item.Category)
                .WithMany()
                .HasForeignKey(item => new { item.CategoryId });
        }   
}

Repository
public class ItemRepo
{
        protected IDbSet<Item> ItemSet;
        protected DataContext Context;

        public ItemRepo(DataContext dataContext)
        {
            Context = dataContext;
            ItemSet = dataContext.Set<Item>();
        }

         public List<Item> GetItems(string from, string to)
        {
            ItemSet.Include(item => item.Category);
            IEnumerable<Item> items = (from a in ItemSet
                                       select a).OrderBy(a =>                  a.Id).Skip(Convert.ToInt32(from)).Take(Convert.ToInt32(to));

            return items.ToList();
        }     
}

WCF Service Method
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public List<Item> GetItems(string From, string To)
{
       var itemRepo = new ItemRepo(new DataContext());
       return itemRepo.GetItems(From, To);
}

Android-Client side

You have to access the WCF Service with a similar function like below. I have used a third party library called gson-1.7.1(not the latest one), which can be downloaded from the below link.
download google-gson

Item-Mapping [since my mapping properties had the same name with compared to the json result item list, i have not used the SerializedName annotation]

Mapping

public class Item {

private String Id;
private String Name;
private String Url;
private int InStock;
private Integer OrderQty;
private Double Price;

public String getId() {
 return Id;
}
public void setId(String id) {
 Id = id;
}
public String getName() {
 return Name;
}
public void setName(String name) {
 Name = name;
}
public Integer getOrderQty() {
 return OrderQty;
}
public void setOrderQty(int orderQty) {
 OrderQty = orderQty;
}
public int getInStock() {
 return InStock;
}
public void setInStock(int inStock) {
 InStock = inStock;
}
public String getUrl() {
 return Url;
}
public void setUrl(String url) {
 Url = url;
}
public Double getPrice() {
 return Price;
}
public void setPrice(Double price) {
 Price = price;
}
}


WCF Service accessing method

public List<Item> getItems(String from, String to) {

   List<Item> items = null;
   Gson gson = new Gson();

   try {

  String urlWithParam = String.format("http://app.dinotait.com/Items/HTML5Service.svc/GetItems?From=%s&To=%s",from, to);
  
  HttpGet request = new HttpGet(urlWithParam);

request.setHeader("Accept", "application/json");
  request.setHeader("Content-type", "application/json");

DefaultHttpClient httpClient = new DefaultHttpClient();
  HttpResponse response = httpClient.execute(request);

HttpEntity responseEntity = response.getEntity();

// Read response data into buffer
  char[] buffer = new char[(int) responseEntity.getContentLength()];
  InputStream stream = responseEntity.getContent();
  InputStreamReader reader = new InputStreamReader(stream);
  reader.read(buffer);
  stream.close();
 
  Type type = new TypeToken<List<Item>>(){}.getType();

  items = gson.fromJson(new String(buffer), type);

} catch (Exception e) {
e.printStackTrace();
}

   return items;
}


Remember that you have to execute this function within an AsyncTask or similar approach since this is a network operation.




Wednesday, February 20, 2013

Add Unallocated Space to an existing partition (Windows 7 32-bit)

I came across a situation like this while i'm working in my office. My office-pc's(OS: Windows 7 32-bit) hard disk had an unallocated space around 30GB. I didn't want to make use of this space to create another separate partition (already having 3 separate partitions), what I wanted is to extend an existing partition with it. What I did was mentioned in step-by-step below.

Open the command-prompt and type diskpart.exe.
This directs you to diskpart shell window DISKPART>

Then select the volume which you want to extend, in my case it was E drive, so I type 7 enter,
DISKPART>  select volume E

It will show,
"Volume 4 is the selected volume" (in my case, it is the E drive)

Then type and enter,
DISKPART> extend noerr
If all goes well, you will be seen "diskpart successfully extended the volume".

sourcehttp://www.ehow.com/how_6797254_add-unallocated-space-partition-xp.html

Wednesday, January 23, 2013

Multiple images uploading (with file size reduced) + MVC4, JQuery mobile, XMLHttpRequest, HTML5 File Api

I had to work with a project that had the functionality of multiple image files uploading. The specialty of the functionality was it had the ability of uploading larger images with reduced file sizes apart from uploading the original files. I thought of sharing the basic functionality of size reduced files-uploading.

Technologies used : MVC4, JQuery mobile, XMLHttpRequest, HTML5 File Api

MVC4 View

@{
    ViewBag.Title = "File Upload";
}

<div>
    <input type="file" accept="image/*" name="fileToUpload[]" id="fileToUpload" style='visibility: hidden;'
        name="img" onchange="fileSelected();" />
    <input type="button" value="Choose image"  onclick="javascript:document.getElementById('fileToUpload').click();">
    <ul data-role="listview" id="uploadfilelist" data-inset="true" data-split-theme="d"
        data-split-icon="delete">
    </ul>
</div>
<div>
    <ul data-role="listview" data-inset="true">
        <li data-role="fieldcontain">
            <input type="button" onclick="uploadFiles();" value="Upload [max. 5 files]" />
        </li>
    </ul>
</div>

There were several javascript functions I have written to fulfill the requirement. 

  • fileSelected - this function is used to show preview of the selected file. Also I have written some validations such as only image types are allowed, maximum file size is 10MB, no.of files that can be uploaded at once cannot be higher than 5. Remember that your browser should be supported HTML5 File Api.
  • uploadFiles - loop through filequeue and popping up the file and the list-element.
  • resizeAndUpload - this function does the size reducing before uploading. At the moment I have used the MAX Height as 600 and MAX Width as 800. I have use the canvas element and redraw the original image with the max height, width dimensions.  XMLHttpRequest object is used to send the data(canvas element can be converted to base64string with the use of toDataURL("image/jpeg")) to server.

javascript code


<script type="text/javascript">

    var filequeue = new Array();

    function fileSelected() {

        try {

            if (window.File && window.FileReader && window.FileList && window.Blob) {
                
                var selectedfile;

                var files = document.getElementById('fileToUpload').files;
                if (filequeue.length > 4) {
                    alert('Maximum number of allowable file uploads has been exceeded!!');
                } else {

                    selectedfile = files[0];

                    if (!selectedfile.type.match('image.*')) {
                        alert('Only image files are allowed');
                    } else if (selectedfile.size > 10485760) {
                        alert('Maximum file size exceeds');
                    }

                    if (selectedfile.type.match('image.*') && selectedfile.size < 10485760) {

                        var reader = new FileReader();

                        reader.onload = (function(theFile) {
                            return function(e) {

                                var li = document.createElement("li");
                                li.setAttribute("data-icon", "delete");

                                var newlink = document.createElement('a');

                                var img = document.createElement("img");
                                img.setAttribute("src", e.target.result);
                                img.setAttribute("height", "80");
                                img.setAttribute("width", "80");
                                newlink.appendChild(img);

                                var h3 = document.createElement("h3");
                                var h3Text = document.createTextNode(theFile.name);
                                h3.appendChild(h3Text);
                                newlink.appendChild(h3);

                                li.appendChild(newlink);

                                document.getElementById("uploadfilelist").appendChild(li);

                                $("#uploadfilelist").listview("refresh");

                                filequeue.push({ file: theFile, lisetElement: li });

                            };
                        })(selectedfile);

                        reader.readAsDataURL(selectedfile);

                    }

                }
                
            }else {
                alert("html5 file api is not compatible with this browser!");
            }

            $("#fileToUpload").val("");

        } catch (err) {
            alert("Exception " + err);
        }
    }


    function uploadFiles() {

        try {

            if (filequeue != null && filequeue.length != 0) {
                
                while (filequeue.length > 0) {

                    var item = filequeue.pop();
                    var file = item.file;
                    var li = item.lisetElement;
                    resizeAndUpload(file, li);

                }
            }

        } catch (err) {
            alert("Exception " + err);
        }

    }

    function resizeAndUpload(file, li) {

        try {

            if (window.File && window.FileReader && window.FileList && window.Blob) {

                var uploadurl = '@Url.Action("UploadSizeReducedFiles", "FileUpload")';

                var reader = new FileReader();
                reader.onloadend = function(evt) {

                    if (evt.target.readyState == FileReader.DONE) {

                        var tempImg = new Image();

                        tempImg.onload = function() {

                            var MAX_WIDTH = 800;
                            var MAX_HEIGHT = 600;
                            
                            var tempWidth = tempImg.width;
                            var tempHeight = tempImg.height;

                            if (tempWidth > tempHeight) {
                                if (tempWidth > MAX_WIDTH) {
                                    tempHeight *= MAX_WIDTH / tempWidth;
                                    tempWidth = MAX_WIDTH;
                                }
                            } else {
                                if (tempHeight > MAX_HEIGHT) {
                                    tempWidth *= MAX_HEIGHT / tempHeight;
                                    tempHeight = MAX_HEIGHT;
                                }
                            }

                            var canvas = document.createElement('canvas');
                            canvas.setAttribute("height", tempHeight);
                            canvas.setAttribute("width", tempWidth);
                            var context = canvas.getContext("2d");
                            context.drawImage(tempImg, 0, 0, tempWidth, tempHeight);

                            var xhr = new XMLHttpRequest();
                            xhr.open("POST", uploadurl);
                            xhr.setRequestHeader("Content-type", "application/json; charset=utf-8");
                            xhr.setRequestHeader("X-File-Name", file.name);
                            var data = 'image=' + canvas.toDataURL("image/jpeg");
                            xhr.send(data);

                            xhr.onreadystatechange = function() {

                                var containtext;

                                if (xhr.readyState != 4) {
                                    return;
                                }

                                else if (xhr.readyState == 4) {

                                    if (xhr.status == 500) {

                                        containtext = $(li).find("h3").text();
                                        $(li).find("h3").text(containtext + " upload error");
                                        $(li).find("h3").css("color", "#FF0000");

                                    }
                                    else if (xhr.status == 200) {

                                        containtext = $(li).find("h3").text();
                                        $(li).find("h3").text(containtext + " upload complete");

                                    }

                                }

                            };

                        };
                        tempImg.src = reader.result;

                    };

                };
                reader.readAsDataURL(file);

            } else {
                alert("html5 file api is not compatible with this browser!");
            }

        }
        catch(err) {
            alert("Exception " + err);
        }
    }

</script>

FileUpload Controller

This method handles the file saving to the server-side folder which is Uploads in my scenario, the status of the uploaded file will be displayed in the file uploading list.

[AcceptVerbs(HttpVerbs.Post)]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public JsonResult UploadSizeReducedFiles()
{
    string fileName = null;
    string uploadedFilePath = null;

    try
    {

        fileName = Request.Headers["X-File-Name"];

        var directoryPath = Server.MapPath("~/Uploads/");

        if (!Directory.Exists(directoryPath))
        {
            Directory.CreateDirectory(directoryPath);
        }
                
        var stream = new StreamReader(Request.InputStream);
        var image = stream.ReadToEnd();

        image = image.Substring("image=data:image/jpeg;base64,".Length);
        var buffer = Convert.FromBase64String(image);

        uploadedFilePath = string.Format("{0}{1}", directoryPath, fileName);

        System.IO.File.WriteAllBytes(uploadedFilePath, buffer);

        return Json(fileName + " completed", JsonRequestBehavior.AllowGet);
    }
    catch (Exception e)
    {
        if (System.IO.File.Exists(uploadedFilePath))
        {
            if (uploadedFilePath != null) System.IO.File.Delete(uploadedFilePath);
        }

        Response.StatusCode = 500;
        return Json(fileName + " fail", JsonRequestBehavior.AllowGet);
    }
}

For functions like size reduce file uploading along with original file uploading, remove selected files before uploading, file uploading progress indicator please contact me via email.