Plan
- Part 1 : Introduction
- Part 2 : Sample CRUD application using Azure tables
- Part 3 : Sample web application using Azure Blobs and Queue
- Part 4 : SQL Azure database
- Part 5 : Deploying your application as PAAS
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 application1) 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 aCloudBlobContainer
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 aCloudQueueMessage
object from available queue messages.- DeleteMessage(
CloudQueueMessage
) : drop givenCloudQueueMessage
from current Queue.
7) CloudQueueMessage
- CloudQueueMessage(
string
) : create a newCloudQueueMessage
instance.
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 :
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 :
1) Web Role
a) Create a Server sideModels :
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 theImageProcessor
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 :
- Download image from cloud after processing :
you will get this new image, which has been modified by our worker role .
- Delete image :
No comments:
Post a Comment