Gaurav Mantri's Personal Blog.

Storage Client Library 2.0 – Migrating Blob Storage Code

A few days ago I wrote blog posts about migrating code from storage client library 1.7 to 2.0 to manage Windows Azure Table and Queue Storage. You can read those posts here: https://gauravmantri.com/2012/11/17/storage-client-library-2-0-migrating-table-storage-code/ and https://gauravmantri.com/2012/11/24/storage-client-library-2-0-migrating-queue-storage-code/.

In this post, I will talk about migrating code from storage client library 1.7 to 2.0 for managing Windows Azure Blob Storage. I would put changes done to manage blob storage somewhere between table storage and queue storage changes. They’re not as drastic as the ones to the table storage however at the same time they are not as simple as changes done to queue storage. Some methods have been removed completely so if your code made use of those functions, you would have to rewrite those portions of code.

Like the previous posts, I will attempt to provide some code sample through which I will try and demonstrate how you can do some common tasks when working with Azure Blob Storage. What I did is wrote two simple console applications: one which uses storage client library version 1.7 and the other which uses version 2.0 and in those two applications I demonstrated some simple functionality.

Read These First

Since version 2.0 library is significantly different than the previous ones, before you decide to upgrade your code to make use of this version I strongly urge you to read up the following blog posts by the storage team as there’re many breaking changes.

Introducing Windows Azure Storage Client Library 2.0 for .NET and Windows Runtime

http://blogs.msdn.com/b/windowsazurestorage/archive/2012/10/29/introducing-windows-azure-storage-client-library-2-0-for-net-and-windows-runtime.aspx

Windows Azure Storage Client Library 2.0 Breaking Changes & Migration Guide

http://blogs.msdn.com/b/windowsazurestorage/archive/2012/10/29/windows-azure-storage-client-library-2-0-breaking-changes-amp-migration-guide.aspx

Getting Started

Before jumping into the code, there’re a few things I would like to mention:

Storage Client Libraries

To get the reference for storage client library 1.7, you can browse your local computer and navigate to the Azure SDK installation directory (C:\Program Files\Microsoft SDKs\Windows Azure\.NET SDK\2012-10\ref – assuming you have SDK 1.8 installed) and select Microsoft.WindowsAzure.StorageClient.dll from there.

To get the reference for storage client library 2.0 (or the latest version for that matter), I would actually recommend getting this using Nuget. That way you’ll always get the latest version. You can simply get it by executing the following command in Nuget Package Manager console: Install-Package WindowsAzure.Storage. While it’s an easy way to get the latest version upgrades, one must not upgrade it before ensuring the new version won’t break anything in the existing code.

Namespaces

One good thing that is done with version 2.0 is that the functionality is now neatly segregated into different namespaces. For blob storage, following 2 namespaces are used:

using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Blob.Protocol;

Blob Request Options and Operation Context

One interesting improvement that has been done is that with every storage client library function, you can pass 2 additional optional parameters: Blob Request Options and Operation Context. Blob Request Options object allows you to control the retry policies (to take care of transient errors) and some of the server side behavior like request timeout. Operation Context provides the context for a request to the storage service and provides additional runtime information about its execution. It allows you to get more information about request/response plus it allows you to pass a client request id which gets logged by storage analytics. For the sake of simplicity, I have omitted these two parameters from the code I included below.

Operations

Now let’s see how you can perform some operations. What I’ve done is first showed how you did an operation with version 1.7 and then how would you do the same operation with version 2.0.

Create Blob Container

If you’re using the following code with version 1.7 to create a blob container:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            container.CreateIfNotExist();

You would use something like this with version 2.0 to achieve the same:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            container.CreateIfNotExists();

Only difference that you will see in the two functions above is version 2.0 is now grammatically correct 🙂 [in 1.7, it is CreateIfNotExist() and in 2.0, it is CreateIfNotExists() –> notice an extra “s”].

Delete Blob Container

If you’re using the following code with version 1.7 to delete a blob container:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            container.Delete();

You would use something like this with version 2.0 to achieve the same:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            container.DeleteIfExists();

One interesting improvement with 2.0 is that it now eats up “Resource Not Found (HTTP Error Code 404)” exception if you’re trying to delete a blob container which does not exist when you use “DeleteIfExists()” function. With 1.7, you don’t have that option and you would need to handle 404 error in your code. Please note that “Delete()” function is still available in 2.0 on a blob container.

List Blob Containers

If you’re using the following code with version 1.7 to list blob containers in a storage account:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            IEnumerable<CloudBlobContainer> blobContainers = cloudBlobClient.ListContainers();

You would use something like this with version 2.0 to achieve the same:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            IEnumerable<CloudBlobContainer> blobContainers = cloudBlobClient.ListContainers();

As you can see, the functionality is same in both versions.

List Blobs

There are many options available to you for listing blobs in a blob container. I will only cover two scenarios here: Listing all blobs in a blob container and listing all blobs which start with specified prefix.

List All Blobs

If you’re using the following code with version 1.7 to list all blobs in a blob container:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            IEnumerable<IListBlobItem> blobs = container.ListBlobs();

You would use something like this with version 2.0 to achieve the same:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            IEnumerable<IListBlobItem> blobs = container.ListBlobs();

As you can see, the functionality is same in both versions.

List All Blobs Which Start With Specified Prefix

If you’re using the following code with version 1.7 to list all blobs in a blob container which starts with specified prefix:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            string prefix = "<Some Blob Prefix>";
            string blobPrefix = containerName + "/" + prefix;
            IEnumerable<IListBlobItem> blobs = cloudBlobClient.ListBlobsWithPrefix(blobPrefix, new BlobRequestOptions()
                {
                    UseFlatBlobListing = true,
                });

With version 2.0, you actually get many options to achieve this. Some of them are:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string prefix = "<Some Blob Prefix>";
            IEnumerable<IListBlobItem> blobs = container.ListBlobs(prefix, true);

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            string prefix = "<Some Blob Prefix>";
            string blobPrefix = containerName + "/" + prefix;
            IEnumerable<IListBlobItem> blobs = cloudBlobClient.ListBlobsWithPrefix(blobPrefix, true);			

Upload Blob

IMO, this is one functionality which has changed considerably between version 1.7 and 2.0. In version 1.7, you had many options to upload a blob (for the sake of simplicity, we’ll restrict ourselves to block blobs only). You could upload a blob from a file, from a byte array, from a text string or a stream. In version 2.0, only option you have is upload from stream. All other methods have been removed. If your code with version 1.7 is making use of the functions which are now removed from 2.0, you could either rewrite those functions to make use of stream or you could write some extension methods. For example, let’s say you’re uploading a blob from a file using the following code with version 1.7:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string filePath = "<Full File Path e.g. C:\temp\myblob.txt>";
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            blob.UploadFile(filePath);

With version 2.0, if you decide to rewrite the above code, you could do something like:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string filePath = "<Full File Path e.g. C:\temp\myblob.txt>";
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            using (FileStream fs = new FileStream(filePath, FileMode.Open))
            {
                blob.UploadFromStream(fs);
            }

Or, you could write an extension method which will encapsulate uploading to stream functionality. For example:

        public static void UploadFile(this CloudBlockBlob blob, string fileName)
        {
            using (FileStream fs = new FileStream(fileName, FileMode.Open))
            {
                blob.UploadFromStream(fs);
            }
        }

Then you would upload blob using this way:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string filePath = "<Full File Path e.g. C:\temp\myblob.txt>";
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            blob.UploadFile(filePath);

Download Blob

Like blob upload, this functionality has also been changed considerably between 1.7 and 2.0. Similar to upload in version 1.7, you had many options to download a blob (again for simplicity, we will focus on block blobs). You could download a blob to a file, to a byte array, to a text string or to a stream. In version 2.0, only option you have is download to stream. All other methods have been removed. If your code with version 1.7 is making use of the functions which are now removed from 2.0, you could either rewrite those functions to make use of stream or you could write some extension methods. For example, let’s say you’re downloading a blob to a file using the following code with version 1.7:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string filePath = "<Full File Path e.g. C:\temp\myblob.txt>";
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            blob.DownloadToFile(filePath);

With version 2.0, if you decide to rewrite the above code, you could do something like:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string filePath = "<Full File Path e.g. C:\temp\myblob.txt>";
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            using (FileStream fs = new FileStream(filePath, FileMode.Create))
            {
                blob. DownloadToStream(fs);
            }

Or, you could write an extension method which will encapsulate downloading from stream functionality. For example:

        public static void DownloadToFile(this CloudBlockBlob blob, string fileName)
        {
            using (FileStream fs = new FileStream(fileName, FileMode.Create))
            {
                blob.DownloadToStream(fs);
            }
        }

Then you would download blob using this way:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string filePath = "<Full File Path e.g. C:\temp\myblob.txt>";
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            blob.DownloadToFile(filePath);

Delete Blob

If you’re using the following code with version 1.7 to delete a blob from a blob container:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            blob.DeleteIfExists();

You would use something like this with version 2.0 to achieve the same:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            blob.DeleteIfExists();

As you can see, the functionality is same in both versions.

Copy Blob

As you know, with the release of Blob Storage Service Version 2012-02-12, copy blob operation is an asynchronous operation. You can read more about it here: http://blogs.msdn.com/b/windowsazurestorage/archive/2012/06/12/introducing-asynchronous-cross-account-copy-blob.aspx. Storage client library version 1.7 did not have support for this functionality however storage team released version 1.7.1 which had support for this functionality. So if you’re using the following code with version 1.7.1 to copy blob:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer sourceContainer = cloudBlobClient.GetContainerReference(containerName);
            CloudBlobContainer targetContainer = cloudBlobClient.GetContainerReference(targetContainerName);
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob sourceBlob = sourceContainer.GetBlockBlobReference(blobName);
            CloudBlockBlob targetBlob = targetContainer.GetBlockBlobReference(blobName);
            targetBlob.CopyFromBlob(sourceBlob);

You would use something like this with version 2.0 to achieve the same:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer sourceContainer = cloudBlobClient.GetContainerReference(containerName);
            CloudBlobContainer targetContainer = cloudBlobClient.GetContainerReference(targetContainerName);
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob sourceBlob = sourceContainer.GetBlockBlobReference(blobName);
            CloudBlockBlob targetBlob = targetContainer.GetBlockBlobReference(blobName);
            targetBlob.StartCopyFromBlob(sourceBlob);

Please note that the function in version 2.0 is “StartCopyFromBlob()” where as in version 1.7, it is “CopyFromBlob()”. Please make a note of that.

Snapshot Blob

If you’re using the following code with version 1.7 to take a snapshot of a blob:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            blob.CreateSnapshot();

You would use something like this with version 2.0 to achieve the same:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            blob.CreateSnapshot();

As you can see, the functionality is same in both versions.

Lease blob

One important improvement that has happened in version 2.0 of storage client library is that it now has full fidelity with the REST API i.e. all the functions exposed through REST API are now available in the storage client library. This was not true with version 1.7. A good example of that is lease blob functionality. In version 1.7, there was no direct function available for acquiring lease on the blob and one would need to resort to the protocol layer (Microsoft.WindowsAzure.StorageClient.Protocol) to perform lease operation. So in order to acquire lease on a blob, if you’re using the following code with version 1.7:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentialsAccountAndKey(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string blobName = "<Blob Name e.g. myblob.txt>";
            StorageCredentials credentials = cloudBlobClient.Credentials;
            Uri transformedUri = new Uri(credentials.TransformUri(blob.Uri.AbsoluteUri));
            var req = BlobRequest.Lease(transformedUri,
                90, // timeout (in seconds)
                LeaseAction.Acquire, // as opposed to "break" "release" or "renew"
                null); // name of the existing lease, if any
            credentials.SignRequest(req);
            using (var response = req.GetResponse())
            {
                string leaseId = response.Headers["x-ms-lease-id"];
            }

With version 2.0, it’s actually quite straight forward and you would use something like this to achieve the same:

            CloudStorageAccount storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, accountKey), true);
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);
            string blobName = "<Blob Name e.g. myblob.txt>";
            CloudBlockBlob blob = container.GetBlockBlobReference(blobName);
            TimeSpan? leaseTime = TimeSpan.FromSeconds(15);//Acquire a 15 second lease on the blob. Leave it null for infinite lease. Otherwise it should be between 15 and 60 seconds.
            string proposedLeaseId = null;//proposed lease id (leave it null for storage service to return you one).
            string leaseId = blob.AcquireLease(leaseTime, proposedLeaseId);

Closing Thoughts

As I mentioned above and demonstrated through examples, there are a few differences in storage client library 1.7 and 2.0 as far as managing blob storage is concerned. Some of them are quite major while others are quite manageable. However in my opinion they are not as drastic as with the tables and the migration should be considerably smooth.

Finally (as I have been saying repeatedly in past 2 posts), don’t give up on Storage Client Library 1.7 just yet. There’re still some components which depend on 1.7 version. Good example is Windows Azure Diagnostics which still depends on the older version at the time of writing this blog. Good thing is that both version 1.7 and 2.0 can co-exist in a project.

Source Code

You can download the source code for this project from here: Sample Project Source Code

Summary

The examples I presented in this post are quite basic but hopefully they should give you an idea about how to use the latest version of storage client library. In general, I am quite pleased with the changes the team has done. Please feel free to share your experience with migration exercise by providing comments. This will help me and the readers of this blog immensely. Finally, if you find any issues with this post please let me know and I will try and fix them ASAP.

Happy Coding!


[This is the latest product I'm working on]