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:
- Why you should use a SAS to upload any binary data from client devices
- How you could generate your own SAS using Mobile Services Server Side scripts
- 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
... | |
</Grid> | |
... | |
<Page.BottomAppBar> | |
<AppBar> | |
<Button Name="btnTakePhoto" Style="{StaticResource PhotoAppBarButtonStyle}" | |
Click="OnTakePhotoClick" /> | |
</AppBar> | |
</Page.BottomAppBar> | |
... | |
</Page> |
- Add the OnTakePhotoClick handler and use the CameraCaptureUI class for taking photo and video
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); | |
} |
- Update the TodoItem class with some properties that will be required to generate the SAS
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; } | |
} |
- Update the OnTakePhotoClick handler to insert the todoitem setting the ContainerName and resourceName for which we want a SAS generated
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 | |
} |
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
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'; | |
} |
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
... | |
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()); | |
} | |
} | |
} |
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