How to upload an Image to Windows Azure Storage using Mobile Services

This post details how to use the Windows Azure SDK for Node from Windows Azure Mobile Services to generate a Shared Access Signature (SAS) and then how to upload your Image (or any file) to Blob Storage directly from your Windows Store App using the Windows Azure Storage Client Library 2.0 for Windows Runtime (CTP)

Background

In my previous post How to Upload an Image using a Blob Storage SAS generated by Windows Azure Mobile Services I detailed:

  1. Why you should use a SAS to upload any binary data from client devices
  2. How you could generate your own SAS using Mobile Services Server Side scripts
  3. How you could use the HttpClient from a Windows Store app to upload your image using the SAS

With the recent inclusion of the Windows Azure SDK for Node in Mobile Services and the announcement of the Windows Azure Storage Client Library 2.0 for Windows Runtime (CTP) the process for performing Steps 2 and 3 are much easier.  This post will detail the updated approach.

Creating your Mobile Service

In this post I will extend the Mobile Services quick start sample. Before proceeding to the next section create a mobile service and download the quickstart as detailed in the tutorial here

Capturing the Image|Media

Our first task is to capture the media we wish to upload. To do this follow the following steps.

  • Add an AppBar to MainPage.xaml with a take photo button to allow us to capture the image
1 2 3 4 5 6 7 8 9 10 11
...
</Grid>
...
<Page.BottomAppBar>
<AppBar>
<Button Name="btnTakePhoto" Style="{StaticResource PhotoAppBarButtonStyle}"
Click="OnTakePhotoClick" />
</AppBar>
</Page.BottomAppBar>
...
</Page>
view raw gistfile1.cs hosted with ❤ by GitHub
  • Add the OnTakePhotoClick handler and use the CameraCaptureUI class for taking photo and video
1 2 3 4 5 6 7 8 9 10 11
using Windows.Media.Capture;
 
 
private async void OnTakePhotoClick(object sender, RoutedEventArgs e)
{
//Take photo or video
CameraCaptureUI cameraCapture = new CameraCaptureUI();
StorageFile media = await cameraCapture.CaptureFileAsync(CameraCaptureUIMode.PhotoOrVideo);
 
 
}
view raw gistfile1.cs hosted with ❤ by GitHub
  • Update the TodoItem class with some properties that will be required to generate the SAS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
public class TodoItem
{
public int Id { get; set; }
 
[DataMember(Name = "text")]
public string Text { get; set; }
 
[DataMember(Name = "complete")]
public bool Complete { get; set; }
 
//Added below for blob sas generation in Mobile Services
 
[DataMember(Name = "containerName")]
public string ContainerName { get; set; }
 
[DataMember(Name = "resourceName")]
public string ResourceName { get; set; }
public string SAS { get; set; }
 
}
view raw gistfile1.cs hosted with ❤ by GitHub
  • Update the OnTakePhotoClick handler to insert the todoitem setting the ContainerName and resourceName for which we want a SAS generated
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
private async void OnTakePhotoClick(object sender, RoutedEventArgs e)
{
//Take photo or video
CameraCaptureUI cameraCapture = new CameraCaptureUI();
StorageFile media = await cameraCapture.CaptureFileAsync(CameraCaptureUIMode.PhotoOrVideo);
 
//add todo item to trigger insert operation which returns item.SAS
var todoItem = new TodoItem() {
ContainerName = "mypics",
ResourceName= media.Name,
Text = "NA",
};
 
await todoTable.InsertAsync(todoItem);
items.Add(todoItem);
 
//TODO: Upload image direct to blob storage using SAS
}
view raw gistfile1.cs hosted with ❤ by GitHub

Generating a Shared Access Signature (SAS) using Mobile Services server-side script

In this step we add sever-side script to generate a SAS on insert operation of the TodoItem table.

To do this perform the following steps:

  • Navigate to your Mobile Service and select the Data Tab, then click on Todoitem

  • Select Script, then the Insert drop down

  • Add the following server side script to create the containerName and generate a blob SAS for the resourceName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
var azure = require('azure');
var qs = require('querystring');
 
function insert(item, user, request) {
var accountName = '<replace with your storage account name>';
var accountKey = '<replace with your storage account key>';
var host = accountName + '.blob.core.windows.net';
var canonicalizedResource = '/' + item.containerName + '/' + item.resourceName;
//Must be lowercase
item.containerName = item.containerName.toLowerCase();
//Create the container if it does not exist
//we will use public read access for the blobs and will use a SAS to upload
var blobService = azure.createBlobService(accountName, accountKey, host);
blobService.createContainerIfNotExists(item.containerName, {publicAccessLevel : 'blob'}, function(error){
if(!error){
// Container exists now define a policy that provides write access
// that starts immediately and expires in 5 mins
var sharedAccessPolicy = {
AccessPolicy:{
Permissions: azure.Constants.BlobConstants.SharedAccessPermissions.WRITE,
//Start: //use for start time in future, beware of server time skew
Expiry: formatDate(new Date(new Date().getTime() + 5 * 60 * 1000)) //5 minutes from now
}
};
//Generate the SAS for your BLOB
var sasQueryString = getSAS(accountName,
accountKey,
canonicalizedResource,
azure.Constants.BlobConstants.ResourceTypes.BLOB,
sharedAccessPolicy);
//full path for resource with sas
item.sas = 'https://' + host + canonicalizedResource + '?' + sasQueryString;
}
else{
console.error(error);
}
request.execute();
});
}
 
function getSAS(accountName, accountKey, path, resourceType, sharedAccessPolicy) {
return qs.encode(new azure.SharedAccessSignature(accountName, accountKey)
.generateSignedQueryString(path, {}, resourceType, sharedAccessPolicy));
}
 
function formatDate(date){
var raw = date.toJSON();
//blob service does not like milliseconds on the end of the time so strip
return raw.substr(0, raw.lastIndexOf('.')) + 'Z';
}
view raw gistfile1.js hosted with ❤ by GitHub

Using the Windows Azure Storage Client Library 2.0 for Windows Runtime (CTP) to upload the Image directly to storage using the SAS

  • Download the Storage Client libraries for Windows 8 click here.
  • Extract and add a reference to Microsoft.WindowsAzure.Storage.winmd your client project
  • Update OnTakePhotoClick handler to update the image directly to blob storage using the CloudBlockBlob,UploadFromStreamAsync and the generated todoitem.SAS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
...
 
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
 
...
 
private async void OnTakePhotoClick(object sender, RoutedEventArgs e)
{
//Take photo or video
CameraCaptureUI cameraCapture = new CameraCaptureUI();
StorageFile media = await cameraCapture.CaptureFileAsync(CameraCaptureUIMode.PhotoOrVideo);
 
if (media != null)
{
//add todo item to trigger insert operation which returns item.SAS
var todoItem = new TodoItem()
{
ContainerName = "mypics",
ResourceName = media.Name,
Text = "NA",
};
await todoTable.InsertAsync(todoItem);
items.Add(todoItem);
 
//Upload image direct to blob storage using SAS and the Storage Client library for Windows CTP
//Get a stream of the image just taken
using (var fileStream = await media.OpenStreamForReadAsync())
{
var sasUri = new Uri(todoItem.SAS);
 
//Our credential for the upload is our SAS token
StorageCredentials cred = new StorageCredentials(sasUri.Query.Substring(1));
CloudBlobContainer container = new CloudBlobContainer(new Uri(string.Format("https://{0}/{1}", sasUri.Host, todoItem.ContainerName)), cred);
CloudBlockBlob blobFromSASCredential = container.GetBlockBlobReference(todoItem.ResourceName);
await blobFromSASCredential.UploadFromStreamAsync(fileStream.AsInputStream());
}
}
}
view raw gistfile1.cs hosted with ❤ by GitHub

Run the application

  • Hit F5 on the application and right click with your mouse to show the app bar
  • Press the Take Photo button
  • Observe that the SAS is returned from your Mobile Service

  • Check your storage account now has a great picture of a fully polished chrome dome capable of reflecting light far better then your average mirror :)

Enjoy,
Nick

  • Pingback: Windows 8 How to upload an Image using a Blob Storage SAS generated by Windows Azure Mobile Services | Nick Harris .NET

  • Pingback: Doporu?ené ?tení za 47. týden | MSDN Blogs

  • Pingback: Links of the week | Jan @ Development

  • Pingback: Reading Notes 2012-11-19 | Matricis

  • Pingback: Reading Notes 2012-11-19 - Impack Alliance

  • http://www.facebook.com/gregg.dingle Gregg Dingle

    When targeting Windows Phone OS 8.0 in a VS 2012 project, I add a reference to Microsoft.WindowsAzure.Storage.winmd, include the namespaces (using Microsoft.WindowsAzure.Storage.Auth and Microsoft.WindowsAzure.Storage.Blob), compile and get the error below. I can see that I’m getting this because of the 3.9.0.0 System.Net.Primitives in the Windows Phone 8 SDK directory…but how do I fix this? I don’t think copying a 4.0 version of System.Net.Primitives into the Windows Phone 8 SDK directory is the correct solution.

    Warning 101 The primary reference “Microsoft.WindowsAzure.Storage” could not be resolved because it has an indirect dependency on the .NET Framework assembly “System.Net.Primitives, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a” which has a higher version “4.0.0.0″ than the version “3.9.0.0″ in the current target framework. C:WindowsMicrosoft.NETFrameworkv4.0.30319Microsoft.Common.targets 1578 5 GreggToDoList

  • Narendra Macha

    At the time of calling

    await todoTable.InsertAsync(todoItem);

    its throwing error that cannot be inserted null value in sas….

  • Ghassen Rjab

    When I open my Mobile Service’s I get this error
    ‘Could not determine whether source control credentials are already set. Error 500′
    I found no solutions in the internet, do you have a solution?