static void

Blob Storage

There are two types, block blobs (<=200Gb) and page blobs (<= 1Tb)

Main operations

Here's a small facade that shows the main operations. There's an enum and class for list results, and the interface is because I substitute a file IO version (below) in development.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
 
namespace MartinWeb.Cloud
{
    public class BlobStore : IBlobStore
    {
        private readonly string _containerName;
        private readonly bool _isPublic;
        private readonly string _connectionString;
 
        public BlobStore(string containerName, bool isPublic = false)
        {
            //container names must be lowercase a-z,0-9 and -, and 3 to 63 chars long
            if (string.IsNullOrWhiteSpace(containerName)) throw new ArgumentNullException();
            if (containerName.Length < 3 || containerName.Length > 63) throw new ArgumentException();
            _containerName = Regex.Replace(containerName.ToLowerInvariant(), @"[^a-z0-9\-]", "");
            _isPublic = isPublic;
            _connectionString = CloudConfigurationManager.GetSetting("StorageConnectionString");
            if (string.IsNullOrEmpty(_connectionString))
            {
                _connectionString =
                    ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString;
            }
        }
 
        private CloudBlobContainer FindContainer()
        {
            var storageAccount = CloudStorageAccount.Parse(_connectionString);
            var client = storageAccount.CreateCloudBlobClient();
            var container = client.GetContainerReference(_containerName);
            container.CreateIfNotExists();
            if (_isPublic)
            {
                container.SetPermissions(
                    new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
            }
            return container;
        }
 
        public void Save(string name, Stream stream)
        {
            if (name.Length > 1024) throw new ArgumentException("Name is too long");
            var container = FindContainer();
            var blockBlob = container.GetBlockBlobReference(name);
            blockBlob.UploadFromStream(stream);
        }
 
        public Stream Read(string name)
        {
            if (name.Length > 1024) throw new ArgumentException("Name is too long");
            var container = FindContainer();
            var blockBlob = container.GetBlockBlobReference(name);
            return blockBlob.OpenRead();
        }
 
        public string ReadAsText(string name)
        {
            if (name.Length > 1024) throw new ArgumentException("Name is too long");
            var container = FindContainer();
            var blockBlob = container.GetBlockBlobReference(name);
            using (var memoryStream = new MemoryStream())
            {
                blockBlob.DownloadToStream(memoryStream);
                var array = memoryStream.ToArray();
                return System.Text.Encoding.UTF8.GetString(array);
            }
        }
 
        public void Delete(string name)
        {
            if (name.Length > 1024) throw new ArgumentException("Name is too long");
            var container = FindContainer();
            var blockBlob = container.GetBlockBlobReference(name);
            blockBlob.DeleteIfExists();
        }
 
        public IEnumerable<ListBlobItem> List(string prefix = null, bool useFlatBlobListing = false)
        {
            //prefix is the subdirectory
            var container = FindContainer();
            foreach (var item in container.ListBlobs(prefix, useFlatBlobListing))
            {
                if (item is CloudBlobDirectory)
                {
                    yield return new ListBlobItem(item, ListBlobType.Directory);
                }
                var blockBlob = item as CloudBlockBlob;
                if (blockBlob != null)
                {
                    yield return new ListBlobItem(item, ListBlobType.BlockBlob) { Length = blockBlob.Properties.Length };
 
                }
                else
                {
                    var cloudPageBlob = item as CloudPageBlob;
                    if (cloudPageBlob != null)
                    {
                        yield return new ListBlobItem(item, ListBlobType.PageBlob) { Length = cloudPageBlob.Properties.Length };
 
                    }
                }
            }
        }
    }
 
    public interface IBlobStore
    {
        void Save(string name, Stream stream);
        Stream Read(string name);
        string ReadAsText(string name);
        void Delete(string name);
        IEnumerable<ListBlobItem> List(string prefix = null, bool useFlatBlobListing = false);
    }
 
    public class ListBlobItem
    {
        public ListBlobItem(IListBlobItem item, ListBlobType blobType)
        {
            Uri = item.Uri;
            ListBlobType = blobType;
        }
 
        public ListBlobItem(string uri)
        {
            Uri = new Uri(uri);
            ListBlobType = ListBlobType.BlockBlob;
        }
 
        public Uri Uri { get; private set; }
        public long Length { get; set; }
        public ListBlobType ListBlobType { get; private set; }
    }
 
    public enum ListBlobType
    {
        BlockBlob,//block blobs <=200Gb, If <64Mb written in single write.
        PageBlob,//page blobs <= 1Tb
        Directory //fake subdirectory (blobs with / in filename)
    }
}

Use

using (var ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes("Hello")))
{
    new BlobStore("MyContainer").Save("Hello.txt", ms);
}

File version

The storage emulator is dead slow, so I prefer to use File IO locally (although the emulator itself uses a directory C:\Users\User\AppData\Local\DevelopmentStorage\LDB\BlockBlobRoot).

using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
 
namespace MartinWeb.Cloud
{
    public class FileStore : IBlobStore
    {
        private readonly string _container;
 
        public FileStore(string containerName)
        {
            var path =
                ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString;
            if (!Directory.Exists(path))
                path = Path.GetTempPath();
            _container = Path.Combine(path, containerName);
            Directory.CreateDirectory(_container);
        }
 
        private static string SanitizeName(string name)
        {
            if (string.IsNullOrEmpty(name)) return null;
            return new string(name.Select(x =>
                Path.GetInvalidFileNameChars().Contains(x) ? '_' : x).ToArray());
        }
 
        public void Save(string name, Stream stream)
        {
            var path = Path.Combine(_container, SanitizeName(name));
            using (var file = File.Create(path))
            {
                stream.CopyTo(file);
            }
        }
 
        public Stream Read(string name)
        {
            var path = Path.Combine(_container, SanitizeName(name));
            return File.OpenRead(path);
        }
 
        public string ReadAsText(string name)
        {
            var path = Path.Combine(_container, SanitizeName(name));
            return File.ReadAllText(path);
        }
 
        public void Delete(string name)
        {
            var path = Path.Combine(_container, SanitizeName(name));
            File.Delete(path);
        }
 
        public IEnumerable<ListBlobItem> List(string prefix = null, bool useFlatBlobListing = false)
        {
            foreach (var file in Directory.GetFiles(_container, SanitizeName(prefix) + "*"))
            {
                yield return new ListBlobItem(file);
            }
        }
    }
}

Concurrency

See MSDN team blog