How To Use Blobs

Basic blob operations

The following sections will walk through the basics of interacting with blob storage within an ASP.NET Web API project:

  • Storage account and container creation, done in a constructor.
  • Uploading a file on the server to a blob.
  • Download a file from a blob to the server.
  • Deleting a blob.
  • Listing metadata about each blob.

These five basic operations should be enough for you to do a large amount of tasks with Blob Storage.

Create a storage account and container

In the constructor, a Storage Account and Container are created as follows:

  1. Parse the connection string (which could be for local development or an actual connection string).
  2. Create a client, which could then be used to create multiple containers.
  3. Create a container by name if it doesn’t already exist, and write diagnostics based on if it already did.
private readonly string CONN_STRING_SETTING = "AzureStorageConnectionString";
private readonly CloudBlobClient _client;
private readonly CloudBlobContainer _container;

// Initialize this controller with storage account and blob container
public BlobsController()
{
    var connString = CloudConfigurationManager.GetSetting(CONN_STRING_SETTING);
    var account = CloudStorageAccount.Parse(connString);

    _client = account.CreateCloudBlobClient();
    _container = _client.GetContainerReference(CONTAINER_NAME);
}

Every blob in Azure Storage must reside in a container. The container name is part of how you uniquely identify any blob in Azure Storage, and is your entry point for any blob operation.

Container names must also be a valid DNS name. To learn more about containers and naming, see Naming and Referencing Containers, Blobs, and Metadata.

Upload to a blob

[Route("api/blobs/upload")]
public async Task UploadFile(string path)
{
    var filePathOnServer = Path.Combine(HostingEnvironment.MapPath(UPLOAD_PATH), path);

    using (var fileStream = File.OpenRead(filePathOnServer))
    {
        var filename = Path.GetFileName(path); // Trim fully pathed filename to just the filename
        var blockBlob = _container.GetBlockBlobReference(filename);

        await blockBlob.UploadFromStreamAsync(fileStream);
    }
}

The UploadFromStreamAsync method is a general-purpose API for uploading from anything which can be exposed as a stream to a blob. There are other APIs available such as UploadTextAsync, UploadFromFileAsync, and UploadFromByteArrayAsync.

Download a blob

[HttpGet]
[Route("api/blobs/download")]
public async Task DownloadFile(string blobName)
{
    var blockBlob = _container.GetBlockBlobReference(blobName);

    var downloadsPathOnServer = Path.Combine(HostingEnvironment.MapPath(DOWNLOAD_PATH), blockBlob.Name);

    using (var fileStream = File.OpenWrite(downloadsPathOnServer))
    {
        await blockBlob.DownloadToStreamAsync(fileStream);
    }
}

The DownloadToStreamAsyncmethod is a general-purpose API for downloading to anything that can be exposed as a stream. There are other APIs available such as DownloadTextAsync, DownloadToFileAsync, and DownloadToByteArrayAsync.

Delete a blob

public async Task Delete(string blobName)
{
    var blob = _container.GetBlobReference(blobName);
    await blob.DeleteIfExistsAsync();
}

DeleteIfExistsAsync will delete a blob if it exists. It returns a Task<bool>, where true indicates that it successfully deleted the blob, and false indicates that the container did not exist. In this case, we can simply ignore it.

You can also use the DeleteAsync method, but you’ll need to handle any exceptions if the blob doesn’t exist yourself.

List blobs in a container

To list blobs in a container, call ListBlobs on the container as done in the HTTP GET call from the API controller:

public async Task<IEnumerable<string>> Get()
{
    var blobsInfoList = new List<string>();
    var blobs = _container.ListBlobs(); // Use ListBlobsSegmentedAsync for containers with large numbers of files
    var blobsList = new List<IListBlobItem>(blobs);

    if (blobsList.Count == 0)
    {
        await InitializeContainerWithSampleData();

        // Refresh enumeration after initializing
        blobs = _container.ListBlobs();
        blobsList.AddRange(blobs);
    }

    foreach (var item in blobs)
    {
        if (item is CloudBlockBlob blob)
        {
            var blobInfoString = $"Block blob with name '{blob.Name}', " +
                $"content type '{blob.Properties.ContentType}', " +
                $"size '{blob.Properties.Length}', " +
                $"and URI '{blob.Uri}'";

            blobsInfoList.Add(blobInfoString);
        }
    }

    return blobsInfoList;
}

You’ve likely noticed that an is expression is needed on the blob item in the for loop. This is because each blob is of type IListBlobItem, which is a common interface implemented by multiple blob types. This tutorial uses Block Blobs, which are the most common kinds of blobs. If you are working with Page Blobs or Blob Directories, you can cast to those types instead.

If you have a large number of blobs, you may need to use other listing APIs such as ListBlobsSegmentedAsync.

Basic blob operations together

Now that you’ve run the the sample application, take a look at the basic operations on Blobs that it performs. All code for interacting with blobs is in a single file, BlobsController.cs.

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.Azure;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Web.Http;
using System.Diagnostics;
using System.IO;
using System.Web.Hosting;

namespace TutorialForStorage.Controllers
{
    public class BlobsController : ApiController
    {
        private readonly string CONN_STRING_SETTING = "AzureStorageConnectionString";
        private readonly string CONTAINER_NAME = "quickstart";
        private readonly string UPLOAD_PATH = "~/Images/";
        private readonly string DOWNLOAD_PATH = "~/Downloads";
        private readonly CloudBlobClient _client;
        private readonly CloudBlobContainer _container;

        // Initialize this controller with storage account and blob container
        public BlobsController()
        {
            var connString = CloudConfigurationManager.GetSetting(CONN_STRING_SETTING);
            var account = CloudStorageAccount.Parse(connString);

            _client = account.CreateCloudBlobClient();
            _container = _client.GetContainerReference(CONTAINER_NAME);
        }

        public async Task<IEnumerable<string>> Get()
        {
            var blobsInfoList = new List<string>();
            var blobs = _container.ListBlobs(); // Use ListBlobsSegmentedAsync for containers with large numbers of files
            var blobsList = new List<IListBlobItem>(blobs);

            if (blobsList.Count == 0)
            {
                await InitializeContainerWithSampleData();

                // Refresh enumeration after initializing
                blobs = _container.ListBlobs();
                blobsList.AddRange(blobs);
            }

            foreach (var item in blobs)
            {
                if (item is CloudBlockBlob blob)
                {
                    var blobInfoString = $"Block blob with name '{blob.Name}', " +
                        $"content type '{blob.Properties.ContentType}', " +
                        $"size '{blob.Properties.Length}', " +
                        $"and URI '{blob.Uri}'";

                    blobsInfoList.Add(blobInfoString);
                }
            }

            return blobsInfoList;
        }

        public string Get(string name)
        {
            // Retrieve reference to a blob by filename, e.g. "photo1.jpg".
            var blob = _container.GetBlockBlobReference(name);
            var blobInfoString = $"Block blob with name '{blob.Name}', " +
                        $"content type '{blob.Properties.ContentType}', " +
                        $"size '{blob.Properties.Length}', " +
                        $"and URI '{blob.Uri}'";
            return blobInfoString;
        }

        [Route("api/blobs/upload")]
        public async Task UploadFile(string path)
        {
            var filePathOnServer = Path.Combine(HostingEnvironment.MapPath(UPLOAD_PATH), path);

            using (var fileStream = File.OpenRead(filePathOnServer))
            {
                var filename = Path.GetFileName(path); // Trim fully pathed filename to just the filename
                var blockBlob = _container.GetBlockBlobReference(filename);

                await blockBlob.UploadFromStreamAsync(fileStream);
            }
        }

        [HttpGet]
        [Route("api/blobs/download")]
        public async Task DownloadFile(string blobName)
        {
            var blockBlob = _container.GetBlockBlobReference(blobName);

            var downloadsPathOnServer = Path.Combine(HostingEnvironment.MapPath(DOWNLOAD_PATH), blockBlob.Name);

            using (var fileStream = File.OpenWrite(downloadsPathOnServer))
            {
                await blockBlob.DownloadToStreamAsync(fileStream);
            }
        }

        public async Task Delete(string blobName)
        {
            var blob = _container.GetBlobReference(blobName);
            await blob.DeleteIfExistsAsync();
        }

        public async Task InitializeContainerWithSampleData()
        {
            var folderPath = HostingEnvironment.MapPath(UPLOAD_PATH);
            var folder = Directory.GetFiles(folderPath);

            foreach (var file in folder)
            {
                await UploadFile(file);
            }
        }
    }
}

I ran into an issue I understand basic blob operations