Sunday, January 31, 2016

windows azure cloud computing - Part 3 : Sample web application using Azure Blobs and Queue



Plan

Introduction

In this chapter, we will make a sample web application that explains how we can create an Azure cloud service that includes web role and worker role, also how we can manipulate Azure blobs and Queue services.

Using the code

Prerequisite 

in what follow, we will define only useful classes and methods that we used to create our sample application

1) CloudStorageAccount class :

represents a windows azure storage account.
Methods  :
  • Parse(String connection_string) : parse a connection string and create a CloudStorageAccount object from connection string parameter.
  • CloudBlobClient CreateCloudBlobClient() : create a Blob service client that allow us to create a blob and containers in our Azure storage.
  • CloudQueueClient CreateCloudQueueClient() : create Queue service client to have access to services of Cloud Queue related to our Azure storage.

2) CloudBlobClient :

Methods :
  • GetContainerReference(string) : Returns a reference to a CloudBlobContainer from given name.

3) CloudBlobContainer :

Methods :
  • boolean CreateIfNotExists() : create table if not exists in the storage and return true if table is recently created, else it will return false.
  • IEnumerable<IListBlobItem> ListBlobs() : return an enumerable list of blobs available into a specific container.
  • CloudBlockBlob GetBlockBlobReference(string blob_name) : get a reference of block blob from a specific container.

4) CloudBlockBlob :

Methods :
  • UploadFromStream(stream) : upload stream to a block blob, if blob exists that will be overwritten.
  • DownloadToStream(stream) : download the content of blob to an existing stream.

5) CloudQueueClient :

Methods :
  • GetQueueReference : return a reference to CloudQueue object from a given name.

6) CloudQueue

  • boolean CreateIfNotExists() : create Queue object if not exists in the storage and return true if Queue is recently created, else it will return false to indicate that the queue is already exists.
  • AddMessage(CloudQueueMessage) : Add a new CloudQueueMessage  to current queue.
  • CloudQueueMessage GetMessage() : get a CloudQueueMessage object from available queue messages.
  • DeleteMessage(CloudQueueMessage) : drop given CloudQueueMessage from current Queue.

7) CloudQueueMessage

  • CloudQueueMessage(string) : create a new CloudQueueMessage instance.
for more information about Block Blob and Cloud Queue you can check the following links :

Coding

0) Environment setup :

a) Create Azure Cloud project :
in this section we will show you different steps to create an azure project that include  web role and  worker role through printed screens :
First you should Create cloud Azure project that include two role :



 
 
After adding two roll, your project tree should look as follow picture :

 
 

the next screens,  show you how you can configure your Azure sotrage account setting, for example to point to your cloud storage account settings.

 
 

 
 

Installing Windows Azure Storage package on your project :
Before you start coding you should download windows azure storage. In my case i used Nuget Package Manager :


 
 
check your project references :


1) Web Role

a) Create a Server side

Models :

AzureBlobManager class :

this class contains a static methods, that allow user to  :
  • Get a reference of Azure storage Account (in our case we used the local storage),
  • Create or get a reference of our container in Azure storage account.
 using System;  
 using System.Net;    
 using System.Collections.Generic;  
 using System.Linq;  
 using System.Web;  
 using Microsoft.WindowsAzure;    
 using Microsoft.WindowsAzure.Storage;    
 using Microsoft.WindowsAzure.Storage.Table;    
 using System.Configuration;  
 using Microsoft.WindowsAzure.Storage.Blob;  
 namespace WebApplicationTables.Models  
 {  
   public class AzureBlobManager  
   {  
     internal const string ContainerName = "natures";  
     private static CloudStorageAccount CreateStorageAccountFromConnectionString(string storageConnectionString)  
     {  
       CloudStorageAccount storageAccount;  
       try  
       {  
         // Retrieve storage account information from connection string.  
         storageAccount = CloudStorageAccount.Parse(storageConnectionString);  
       }  
       catch (FormatException)  
       {  
         Console.WriteLine("Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the app.config file - then restart the application.");  
         throw;  
       }  
       catch (ArgumentException)  
       {  
         Console.WriteLine("Invalid storage account information provided. Please confirm the AccountName and AccountKey are valid in the app.config file - then restart the sample.");  
         Console.ReadLine();  
         throw;  
       }  
       return storageAccount;  
     }  
     public static CloudBlobContainer CreateContainer()  
       {  
         CloudStorageAccount storageAccount = CreateStorageAccountFromConnectionString(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ToString());  
         // Create a blob client for interacting with the blob service.  
         CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();  
         //trying to Create Container  
         CloudBlobContainer container = blobClient.GetContainerReference(ContainerName);  
         try  
         {  
            container.CreateIfNotExists();  
         }  
         catch (StorageException)  
         {  
           return null;  
         }  
         return container;  
       }  
   }  
 }  

Controllers :

RestAzureBlobsMethodsController class :
this class inherits from ApiController, that implement Restful services that permit to :
  • add new image to an existing container.
  • download image.
  • delete an existing image.
  • retrieve all image from an existing container.

 using Microsoft.WindowsAzure;  
 using Microsoft.WindowsAzure.Storage;  
 using Microsoft.WindowsAzure.Storage.Blob;  
 using Microsoft.WindowsAzure.Storage.Queue;  
 using Newtonsoft.Json;  
 using System;  
 using System.Collections.Generic;  
 using System.Configuration;  
 using System.Drawing;  
 using System.IO;  
 using System.Linq;  
 using System.Net;  
 using System.Net.Http;  
 using System.Net.Http.Headers;  
 using System.Web;  
 using System.Web.Http;  
 using System.Web.Script.Serialization;  
 using System.Web.Services;  
 using WebApplicationTables.Models;  
 namespace WebRole1.Controllers  
 {  
   public class RestAzureBlobsMethodsController : ApiController  
   {  
     //to save some details about blob  
     public class BlobDetails{  
       // public string blobThumbUri;   
       public string blobName;   
     }  
     //get all blob Details  
     [HttpGet]  
      public IEnumerable<BlobDetails> ScanContainer(){  
       HttpRequest request = HttpContext.Current.Request;  
       List<BlobDetails> listBlobDetails = new List<BlobDetails>();  
       try  
       {    
         var container = AzureBlobManager.CreateContainer();  
         if(container != null)  
         {  
           //Get List Blobs in existing Container  
           foreach (IListBlobItem blob in container.ListBlobs())  
           {  
             //this cast allow us to can acces to Blob attributes  
             CloudBlockBlob image = (CloudBlockBlob)blob;  
             image.FetchAttributes();  
             listBlobDetails.Add(new BlobDetails() { blobName = image.Name});  
           }  
         }  
       }  
       catch (Exception e)  
       {  
         ;  
       }  
       return listBlobDetails;  
     }  
     //delete exiting blobs:  
      [HttpGet]  
     public HttpResponseMessage deleteBlob()  
     {  
       HttpRequest request = HttpContext.Current.Request;  
       var status = true;  
       try {   
         //trying to create a unique Image Name to avoid erasing   
         var container = AzureBlobManager.CreateContainer();  
         var imageName = request.Params["imageName"];  
         var blockBlob = container.GetBlockBlobReference(imageName);  
         blockBlob.Delete();  
         //delete thumbnail image  
         var imageServerPath = HttpContext.Current.Server.MapPath("~/Content/imageThumbnails");  
         var thumbnailSrc = System.IO.Path.Combine(imageServerPath, imageName);  
         if (System.IO.File.Exists(thumbnailSrc))   
         {   
           System.IO.File.Delete(thumbnailSrc);  
         }  
       }  
       catch (Exception e)  
       {  
         status = false;  
       }  
       if(status == true)  
       {  
         return new HttpResponseMessage(HttpStatusCode.OK);  
       }else{  
         return new HttpResponseMessage(HttpStatusCode.ExpectationFailed);  
       }  
     }  
     //upload image : when you upload a new image you must push a work for role worker to create a thumb image with 150*150 size  
     [HttpPost]  
     public HttpResponseMessage uploadBlob()  
     {  
       HttpRequest request = HttpContext.Current.Request;  
       var status = true;  
       try  
       {  
         //create a unique Image Name to avoid erasing   
         HttpPostedFile file = request.Files.Get(0);  
         var container = AzureBlobManager.CreateContainer();  
         var imageName = System.IO.Path.GetRandomFileName().Replace('.', ' ') + System.IO.Path.GetExtension(file.FileName);  
         //save entire image  
         var blockBlob = container.GetBlockBlobReference(imageName);  
         blockBlob.Properties.ContentType = MimeMapping.GetMimeMapping(System.IO.Path.GetExtension(imageName));  
         blockBlob.UploadFromStream(file.InputStream);  
         blockBlob.SetProperties();  
         //save thummbnails images  
         var imageServerPath = HttpContext.Current.Server.MapPath("~/Content/imageThumbnails");  
         var thumbnailDst = System.IO.Path.Combine(imageServerPath, imageName);  
         Image image = Image.FromStream(file.InputStream);  
         Image thumb = image.GetThumbnailImage(100, 100, () => false, IntPtr.Zero);  
         thumb.Save(thumbnailDst);  
         //create gray image   
         // Retrieve storage account from connection string  
         CloudStorageAccount storageAccount = CloudStorageAccount.Parse(  
           ConfigurationManager.ConnectionStrings["StorageConnectionString"].ToString());  
         CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();  
         // Retrieve a reference to a queue  
         CloudQueue queue = queueClient.GetQueueReference("myqueue");  
         // Create the queue if it doesn't already exist  
         queue.CreateIfNotExists();  
         // Create a message and add it to the queue.  
         CloudQueueMessage message = new CloudQueueMessage(imageName);  
         queue.AddMessage(message);  
       }  
       catch (Exception e)  
       {  
         status = false;  
       }  
       if (status == true)  
       {  
         return new HttpResponseMessage(HttpStatusCode.OK);  
       }  
       else  
       {  
         return new HttpResponseMessage(HttpStatusCode.ExpectationFailed);  
       }  
     }  
     [HttpGet]  
     public HttpResponseMessage downloadImage()  
     {  
       HttpRequest request = HttpContext.Current.Request;  
       var status = true;  
       MemoryStream memoryStream = new MemoryStream();  
       var imageName = request.Params["imageName"];  
       try  
       {  
         //trying to create a unique Image Name to avoid erasing   
         var container = AzureBlobManager.CreateContainer();  
         var blockBlob = container.GetBlockBlobReference(imageName);  
         blockBlob.DownloadToStream(memoryStream);  
        }  
       catch (Exception e)  
       {  
         status = false;  
       }  
       if (status == true)  
       {  
         var response = new HttpResponseMessage(HttpStatusCode.OK);  
         response.Content = new ByteArrayContent(memoryStream.ToArray());  
         response.Content.Headers.ContentType = new MediaTypeHeaderValue(MimeMapping.GetMimeMapping(System.IO.Path.GetExtension(imageName)));  
         response.Content.Headers.Add("x-filename", imageName);  
         response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");  
         response.Content.Headers.ContentDisposition.FileName = imageName;  
         return response;  
       }  
       else  
       {  
         return new HttpResponseMessage(HttpStatusCode.ExpectationFailed);  
       }  
     }  
   }  
 }  

b) Create a Client side

  • JavaScript code :
 <script>  
   var app = angular.module('myApp', []);  
   app.factory('DataService', ['$http', function ($http) {  
     var imageDetailsList = function () {  
       return $http.get("api/RestAzureBlobsMethods/ScanContainer");  
     }  
     var uploadImage = function (fd) {  
       return $http.post("api/RestAzureBlobsMethods/uploadBlob", fd, {  
         transformRequest: angular.identity,  
         headers: { 'Content-Type': undefined }  
       });  
     }  
     var deleteImage = function (imageName) {  
       var parameters = {  
         imageName : imageName,  
       };  
       var config = {  
         params: parameters  
       };  
       return $http.get("api/RestAzureBlobsMethods/deleteBlob", config);  
     }  
     var downloadImage = function (imageName) {  
       var parameters = {  
         imageName: imageName,  
       };  
       var config = {  
         params: parameters  
       };  
       return $http.get("api/RestAzureBlobsMethods/downloadImage", config);  
     }  
     return {  
       imageDetailsList: imageDetailsList,  
       uploadImage: uploadImage,  
       deleteImage: deleteImage,  
       downloadImage: downloadImage  
     }  
   }]);  
   app.controller("MainCtrl", ["$scope", "DataService",  
     function ($scope, DataService) {  
       getImages();  
       function getImages() {  
         DataService.imageDetailsList().then(function (response) {  
           $scope.imageList = response.data;  
         });  
       }  
       $scope.uploadImage = function() {  
         var fd = new FormData($('#formUploadFile')[0]);  
         DataService.uploadImage(fd).then(function (response) {  
           getImages();  
         });  
       }  
       $scope.deleteImage = function (imageName) {  
         DataService.deleteImage(imageName).then(function (response) {  
           getImages();  
         });  
       }  
       $scope.downloadImage = function (imageName) {  
         DataService.downloadImage(imageName).then(function (response) {  
          });  
       }  
     }  
   ]);  
   $(document).on("click", "#addbutton", function () {  
     //animation for opening and closing insertion form  
     $("#PanelToHide").fadeToggle();  
   });  
 </script>  

  • Html code :

 <div ng-app="myApp" ng-controller="MainCtrl">  
   <div class="row">  
     <div class="col-md-6">  
       <div class="panel panel-primary">  
         <div class="panel-heading">  
           <h3 class="panel-title">Available Images in Azure container titled natures</h3>  
           <div class="pull-right">  
             <span class="clickable filter" id="addbutton" data-toggle="tooltip" title="Toggle table filter" data-container="body">  
               <i class="glyphicon glyphicon-plus-sign"></i>  
             </span>  
           </div>  
         </div>  
         <div id="PanelToHide" class="panel-body" >  
           <form class="form-horizontal" id="formUploadFile" name="formUploadFile">  
             <div class="form-group">  
               <label class="col-sm-2 control-label">File</label>  
               <div class="col-sm-10">  
                 <input type="file" id="myFile" name="myFile" />  
               </div>  
             </div>  
             <div class="form-group">  
               <div class="col-sm-offset-2 col-sm-10">  
                 <button type="submit" ng-click="uploadImage()" class="btn btn-default">Save</button>  
               </div>  
             </div>  
           </form>  
         </div>  
         <table class="table table-hover" id="dev-table">  
           <thead>  
             <tr>  
               <th>Thumbnail</th>  
               <th>Image Name</th>  
               <th></th>  
             </tr>   
           </thead>  
           <tbody ng-repeat="elem in imageList">  
             <tr>  
               <td><img class="img-thumbnail" alt="ressource not found" ng-src="/Content/imageThumbnails/{{elem.blobName}}"></i></img></td>  
               <td>{{elem.blobName}}</td>  
               <td><button type="button" ng-click="deleteImage(elem.blobName)" class="btn btn-default">delete</button>   
               <a role="button" href="./api/RestAzureBlobsMethods/downloadImage/?imageName={{elem.blobName}}" class="btn btn-default">download</a></td>  
             </tr>  
           </tbody>  
         </table>  
       </div>  
     </div>  
   </div>  
 </div>  

2) Worker role

In this example, i used the ImageProcessor class for processing the original image by applying some rotation and filter. you can download ImageProcessor package by using Nuget Package Manager.


 
 

 
 

I only implement the Run() method in WorkerRole.cs, because it's the main body of our background task.

 public override void Run()  
     {  
       Trace.TraceInformation("WorkerRole1 is running");  
       // Retrieve storage account from connection string  
       CloudStorageAccount storageAccount = CloudStorageAccount.Parse(  
         CloudConfigurationManager.GetSetting("StorageConnectionString"));  
       // Create the queue client  
       CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();  
       // Retrieve a reference to a queue  
       CloudQueue queue = queueClient.GetQueueReference("myqueue");  
       queue.CreateIfNotExists();  
       while (true)  
       {  
         // Get the next message  
         CloudQueueMessage retrievedMessage = queue.GetMessage();  
         //Process the message in less than 30 seconds, and then delete the message  
         if (retrievedMessage != null) {   
         var BlobName = retrievedMessage.AsString;  
         Trace.WriteLine("Start Processing image : " + BlobName);  
         queue.DeleteMessage(retrievedMessage);  
         MemoryStream memoryStream = new MemoryStream();  
         try  
         {  
           //trying to create a unique Image Name to avoid erasing   
           var container = AzureBlobManager.CreateContainer();  
           var blockBlob = container.GetBlockBlobReference(BlobName);  
           blockBlob.DownloadToStream(memoryStream);  
           //this example i picked from https://naimhamadi.wordpress.com/2014/06/25/processing-images-in-c-easily-using-imageprocessor/  
           int quality = 50;//i sepcify quality of compression  
           using (var outStream = new MemoryStream())  
           {  
             // Initialize the ImageFactory using the overload to preserve EXIF metadata.  
             using (var imageFactory = new ImageFactory(preserveExifData: true))  
             {  
               // I do some filter and translation of my original image  
               imageFactory.Load(memoryStream.ToArray())  
                 .Rotate(90f)  
                 .Hue(20)  
                 .RoundedCorners(new RoundedCornerLayer(100, true, true, true, true))  
                 .Quality(quality)  
                 .Save(outStream);  
               //Update Blob  
               blockBlob.UploadFromStream(outStream);  
             }  
           }  
         }  
         catch (Exception e)  
         {  
           Thread.Sleep(500);  
         }  
         }  
       }  
     }  

3) Scenario of execution

  • Show all images of an exiting container from Azure storage account :


  • Upload image on cloud :
 First you must open the insertion form by clicking on . next you should select an image, and click on save button.

 
 
Result :


  • Download image from cloud after processing :
First you should click on download button of a chosen image.

you will get the download dialog, click on save button, to download it,


 
 

you will get this new image, which has been modified by our worker role .


  • Delete image :
click on delete button of a chosen image.


Conclusion

The next tutorial will define different methods of creating an SQL Azure Database.

No comments:

Post a Comment