Card image cap

Reading a simple File from Azure Storage in an ASP.NET Core Application

ASP.NET Core Azure  • Posted 5 months ago

Microsoft Azure Storage is a storage service offered by Microsoft Azure as a part of its Cloud Suite of tools and services, which provides a high speed, secure and reliable data storage option for applications. The Blob Storage is also used internally by Azure to write instance level or application server level logs when configured. Azure Storage provides storage options for developers to write or read data from various data structures such as Tables, File Containers or Queues. There are options for developers to manually upload or download data from the Storage by means of the Azure Portal, or by Azure Storage Explorer - and on the other hand applications can write and read data stored in Azure Storage by means of the Azure Storage SDK. In this article, let's talk about how we can access files from an existing Azure Storage Container via Storage SDK in an aspnetcore application.

Getting Started:

To keep up with the programming good practices, let's first create an abstraction - an interface IFileManager which declares two methods ReadFrom(fileName) and WriteTo(fileName, content) as below:


namespace ReadersMvcApp.Providers.FileManager
{
    public interface IStorageManager
    {
        Task<string> WriteTo(string fileName, string content);
        Task<string> ReadFrom(string fileName);
    }
}

And we have our AzureBlobStorageManager class implement this interface with functionality to ReadFrom and WriteTo Azure Storage Container.


namespace ReadersMvcApp.Providers.FileManager
{
    public class AzureBlobStorageManager : IStorageManager
    {
        private readonly IConfiguration _config;

        public AzureBlobStorageManager(IConfiguration configuration)
        {
            _config = configuration;
        }

        /// <summary>
        /// WriteTo
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        public async Task<string> WriteTo(string fileName, string content)
        {
        }
        /// <summary>
        /// ReadFrom
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public async Task<string> ReadFrom(string fileName)
        {
        }
    }
}

On the Azure Side:

Applications access the contents of Azure Storage by means of a ConnectionString, which can be obtained from the Azure Storage under the Containers section.

In our case, we shall obtain two required information items from Azure Storage Portal in order for the Azure Storage to allow access connection from our application.

Click on Storage Accounts on the left menu bar enclosed under a hamburger menu and you'll be redirected to the Storage section. Click on the Storage account under which the container to be accessed resides and click on Access Keys under the Settings menu. You'll be taken to an Access Keys page with two sets of Keys; Key 1 and Key 2. Copy the ConnectionString under Key 1 section (which is a sort of the primary) and keep it aside. This ConnectionString is what provides us the necessary access to the contents under the Storage Account.

data/Admin/2020/5/access-keys.PNG

Take a note of the Container under which we'd access our file and keep these values in the appsettings.json as below:


"AzureBlobStorage": {
    "ConnectionString": "<azure-storage-account-connection-string>",
    "ContainerName":"<azure-container-name>"
}

We'll use these values in our AzureBlobStorageManager class to Read from the Container under the Storage account via this ConnectionString.

We'd next install the required Storage SDK which we shall use in our requirement. The one we use is as below:


<PackageReference Include="WindowsAzure.Storage" Version="9.3.3" />

or


dotnet add package WindowsAzure.Storage --version 9.3.3

The WindowsAzure.Storage package contains the necessary libraries for Storage access from Azure for all sorts of data structures such as Azure Table Storage, Containers and Queues.

For us to access the file from Storage, we'd follow these steps:

  1. Create Connection with the Storage using the ConnectionString
  2. Get Specified Container Instance from the Storage
  3. Read the given fileName within the Container using the created Container Instance

These steps are done in an asynchronous manner, and so our methods are labelled async returning Tasks.

For the first two step, we create a Connection as below:


string connString = _config["AzureBlobStorage:ConnectionString"].ToString();
string containerName = _config["AzureBlobStorage:ContainerName"].ToString();
CloudStorageAccount account = null;
CloudBlobContainer container = null;

// Lookup for a CloudStorageAccount for the given ConnectionString
// if available fetch out the Account reference
// under the account
if (CloudStorageAccount.TryParse(connString, out account))
{
    // create a Client to access the Containers from the Account
    CloudBlobClient cloudBlobClient = account.CreateCloudBlobClient();

    // get the container reference from the account via the client
    container = cloudBlobClient.GetContainerReference(containerName);

At this point, we get a container reference assigned to the variable container, which may or may not exist (considering the error scenario). If not exists, we might be interested in creating a container, which we shall come to as we move forward.

Now that we have access to the container, we'll try to read a file from the container. This we achieve by calling on a method container.GetBlockBlobReference(fileName) to which we pass on the file name which we're interested in. The method returns a BlockReference which contains the data and the metadata information (lastmodified, keys and so on) for the stored file.

To get the data, we shall call the method DownloadTextAsync() on the BlockReference which shall return the content of the file as a string. We can then use this string to parse into whatever format depending on the kind of data present.

The complete functionality is as follows. This method uses the namespaces Microsoft.WindowsAzure.Storage and Microsoft.WindowsAzure.Storage.Blob packages.


public async Task<string> ReadFrom(string fileName)
{
    string data = string.Empty;

    try
    {
        string blobStorageConnString = _config["AzureBlobStorage:ConnectionString"].ToString();
        string containerName = _config["AzureBlobStorage:ContainerName"].ToString();
                
        CloudStorageAccount account = null;
                
        // Lookup for a CloudStorageAccount for the given ConnectionString
        // if available fetch out the Account reference
        // under the account
        
        if (CloudStorageAccount.TryParse(blobStorageConnString, out account))
        {
        
            // create a Client to access the Containers from the Account
            CloudBlobClient cloudBlobClient = account.CreateCloudBlobClient();

            // get the container reference from the account via the client
            CloudBlobContainer container = cloudBlobClient.GetContainerReference(containerName);

            // get the BlockReference from the Container reference
            CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);

            // read data as string and return
            data = await blockBlob.DownloadTextAsync();
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }

    return data;
}

We can then use this handler in our application under the abstraction of IFileManager by declaring as a service.


services.AddSingleton<IFileManager, AzureBlobStorageManager>();

And used in some calling method via Dependency Injection as:


// some functionality base
IFileManager manager = services.GetRequiredService<IFileManager>();
var data = await manager.ReadFrom("myFile.txt");

This way, we can read content from Azure Storage by means of WindowsAzure.Storage SDK for a specified Container and file.

What is the difference between Response.Redirect() and Server.Transfer() ?
How do you handle errors Globally in ASP.NET Core?
How do you design a strongly-typed class for a configuration?
How can you bind a configuration section to an object?
When to use IOptionsMonitor?
We use cookies to provide you with a great user experience, analyze traffic and serve targeted promotions.   Learn More   Accept