Get started with Azure Blob storage using .NET

[!INCLUDE storage-selector-blob-include]

This tutorial will show you how to use Azure Blob Storage with ASP.NET and Visual Studio 2017. It will show how to upload, list, download, and delete blobs in the context of a simple Web API project that works with images.

Get the sample project

To begin, clone the sample project from GitHub. The rest of the tutorial will work with this project.

If you’re unfamiliar with git, you can also download the project as a zip file. In the GitHub repository, there is a green button called Clone or download. You can click that and select Download ZIP:

Screenshot of GitHub showing how to clone or download the sample application

Set up your environment

Ensure that you have Visual Studio 2017 installed with the ASP.NET and web development and Azure Development workloads. This will give you all of the tools that you will need for this tutorial.

Screenshot of searching for the Azure Storage Emulator via Windows search

Run the application locally

First, start the Azure Storage Emulator by pressing the Windows key on your keyboard and searching for Azure Storage Emulator:

Screenshot of searching for the Azure Storage Emulator via Windows search

This will start the Azure Storage Emulator on your machine, and open a command prompt which you can use to control it from there.

Open the sample application in Visual Studio 2017. Start it by pressing f5 in Visual Studio. This will launch the web app on http://localhost:58673/ so that you can interact with it or click the API tab to browse the reference.

Next, enter the following in your browser to test that your GET API is working:

http://localhost:58673/api/blobs

You should see a response that is similar to the following (if viewed as JSON):

[
    "Block blob with name 'Awesome-Mountain-River-Wallpaper.jpg', content type 'application/octet-stream', size '1465150', and URI 'http://127.0.0.1:10000/devstoreaccount1/quickstart/Awesome-Mountain-River-Wallpaper.jpg'",
    "Block blob with name 'Beautiful Stream Desktop Wallpapers (2).jpg', content type 'application/octet-stream', size '382544', and URI 'http://127.0.0.1:10000/devstoreaccount1/quickstart/Beautiful Stream Desktop Wallpapers (2).jpg'",
    "Block blob with name 'Forest-river-wallpaper-Hd.jpg', content type 'application/octet-stream', size '2690689', and URI 'http://127.0.0.1:10000/devstoreaccount1/quickstart/Forest-river-wallpaper-Hd.jpg'",
    "Block blob with name 'Grand-Canyon-Colorado-River.jpg', content type 'application/octet-stream', size '261788', and URI 'http://127.0.0.1:10000/devstoreaccount1/quickstart/Grand-Canyon-Colorado-River.jpg'",
    "Block blob with name 'Smith-River.jpg', content type 'application/octet-stream', size '519207', and URI 'http://127.0.0.1:10000/devstoreaccount1/quickstart/Smith-River.jpg'",
    "Block blob with name 'grand-canyon-colorado-river-1.jpg', content type 'application/octet-stream', size '232356', and URI 'http://127.0.0.1:10000/devstoreaccount1/quickstart/grand-canyon-colorado-river-1.jpg'",
    "Block blob with name 'ws_Columbia_Mountain_River_Grass_1366x768.jpg', content type 'application/octet-stream', size '450769', and URI 'http://127.0.0.1:10000/devstoreaccount1/quickstart/ws_Columbia_Mountain_River_Grass_1366x768.jpg'"
]

Basic blob operations

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

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);
            }
        }
    }
}

Run the sample application in Azure

To publish the application this sample application to Azure, you’ll need an Azure Storage account. The easiest way to do this is with Visual Studio Connected Services.

Create a storage account with Connected Services

  1. In Solution Explorer, right-click on Connected Services.
  2. From the context menu, select Add > Connected Service.
  3. In the Connected Services dialog box, select Cloud Storage with Azure Storage, and then select Create a new Storage Account.

A screenshot of creating a Storage account with Visual Studio Connected Services

  1. In the Azure Storage dialog box, complete the form. Under Resource Group, select Create new. Under Location, pick anything you like.
  2. Once your account has been created, click Add. This will install all the necessary dependencies that you’ll need to get started.

Add the connection string to the release configuration file.

After you’ve added a service reference through Connected Services, it will have placed a true connection string in the appSettings section of your Web.config like so:

<appSettings>
    <add key="AzureStorageConnectionString" value="UseDevelopmentStorage=true" />

    <!-- Azure Storage: STORAGE_NAME -->
    <add key="<STORAGE_NAME>_AzureStorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=STORAGE_NAME;AccountKey=<ACCOUNT_KEY_HERE>;EndpointSuffix=core.windows.net" />
</appSettings>

Replace the "UseDevelopmentStroage=true" value with the real connection string generated by Connected Services.

Publish to Azure

Finally, you can publish to Azure!

  1. In Solution Explorer, right-click and select Publish.
  2. Under Microsoft Azure App Service, ensure that Create New is selected and press the Publish button.
  3. Fill out the form - all fields should be automatically populated with values that will allow you to create a new App Service web app with the project.
  4. Click Create.

After it creates the App Service web app, and the application is fully published, a browser window will open and you can interact with the sample app live over the internet!

Next steps

Now that you’ve learned the basics of Blob storage, follow these links to learn more.

Microsoft Azure Storage Explorer

More samples

Blob storage reference

Conceptual Guides