Pretty much any project that allows file uploads is going to utilize some cloud storage provider. It could be rackspace, it could be GCloud, it could be AWS S3, or in this post’s case, it’s going to be Azure Blob Storage.
Using Blob Storage in .NET Core really isn’t that different to using it in the full framework, but it’s worth quickly going through it with some quickfire examples.
Getting Required Azure Details
By this stage, you should already have an Azure account set up. Azure has a free tier for almost all products for the first year so you can usually get some small apps up and running completely free of charge. Blob Storage actually doesn’t have a free tier, but if you upload just a single file it’s literally cents to store a GB of data. Crazy. I should also note that if you have a MSDN subscription from your work, then you get $150 a month in Azure credits for the lifetime of your MSDN subscription.
Once you have your account setup, you need to head into the Azure portal and follow the instructions to create a storage account. Once created, inside your storage account settings you need to find your access keys.
The end screen should look like this (Minus the fact I’ve blurred out my keys) :
Write down your storage name and your keys, you will need these later to connect via code to your storage account.
Project/Nuget Setup
For this tutorial I’m just working inside a console application. That’s mostly because it’s easier to try and retry things when we are working on something new. And it means that we don’t have to fiddle around with other ASP.net boilerplate work. It’s just open our main and start writing code.
Inside your project, you need to run the following commands from your Nuget Package Manager.
Install-Package WindowsAzure.Storage
This is all you need! It should be noted that the same package will be used if you are developing on full framework too.
Getting/Creating A Container
Containers are just like S3 Buckets in Amazon, they are a “bucket” or “collection” to throw your files into. You can create containers via the portal and some might argue this is definitely safer, but where is the fun in that! So let’s get going and create our own container through code.
First, we need to create the object to hold all our storage account details and then create a “client” to do our bidding. The code looks a bit like this :
var storageCredentials = new StorageCredentials("myAccountName", "myAccountKey"); var cloudStorageAccount = new CloudStorageAccount(storageCredentials, true); var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
When we create our storage credentials object, we pass it in the account name and account key we retrieved from the Azure portal. We then create a cloud storage account object – the second param in this constructor is whether we want to use HTTPS. Unless there is some specific reason you don’t… just put true here. And then we go ahead and create our client.
Next we actually want to create our container via code. It looks a bit like this :
var container = cloudBlobClient.GetContainerReference("mycontainer"); await container.CreateIfNotExistsAsync();
That container object is going to be the keys to the world. Almost everything you do in blob storage will be run off that object.
So our full C# code for our console app that creates a container called “mycontainer” in our Azure Cloud Storage account looks like the following :
class Program { static void Main(string[] args) { Task.Run(() => MainAsync(args)).GetAwaiter().GetResult(); } static async void MainAsync(string[] args) { var storageCredentials = new StorageCredentials("myAccountName", "myAccountKey"); var cloudStorageAccount = new CloudStorageAccount(storageCredentials, true); var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient(); var container = cloudBlobClient.GetContainerReference("mycontainer"); await container.CreateIfNotExistsAsync(); } }
It’s important to note that every remote action in the storage library is async. Therefore I’ve had to do a quick wrap of an async method in our main entry point to get async working. Obviously when you are working inside ASP.net Core you won’t have this issue (And as a side note, from C# 7.1 you also won’t have an issue as they are adding async entry points to console applications to get around this wrapper).
Running this code, and viewing the overview of the storage account, we can see that our container has been created.
Uploading A File
Uploading a blob is incredibly simple. If you already have the file on disk, you can upload it simply by creating a reference to your blob (That doesn’t exist already), and uploading.
var newBlob = container.GetBlockBlobReference("myfile"); await newBlob.UploadFromFileAsync(@"path\myfile.png");
You can also upload direct from a stream (Great if you are doing a direct pass through of a file upload on the web), or even upload a raw string.
The important thing to remember is that in the cloud, a file is just a blob. While you can name your files in the cloud with file extensions, in the cloud it really doesn’t care about mimetypes or anything along those lines. It’s just a bucket to put whatever you want in.
Downloading A File
Downloading a file via C# code is just as easy.
var newBlob = container.GetBlockBlobReference("myfile"); await newBlob.DownloadToFileAsync("path/myfile.png", FileMode.Create);
Similar to uploading a file, you can download files to a string, stream or byte array also.
Leasing A File
Leasing is an interesting concept and one that has more uses than you might think. The original intent is that while a user has downloaded a file and is possibly editing it, you can ensure that no other thread/app can access that file for a set amount of time. This amount of time is a max of 1 minute, but the lease can be renewed indefinitely. This makes sense and it means if you are building some complex file management system, it is definitely handy.
But actually leasing has another great use and that is handling race conditions across apps that have been scaled out. You might typically see Redis used for this, but blob storage also works too.
Consider the following code :
var newBlob = container.GetBlockBlobReference("mylease"); if(! await newBlob.ExistsAsync()) { await newBlob.UploadTextAsync("lease"); } await newBlob.AcquireLeaseAsync(TimeSpan.FromSeconds(30)); await newBlob.AcquireLeaseAsync(TimeSpan.FromSeconds(30));
When the second AcquireLease runs, an exception is thrown that a lease cannot be obtained – and can’t be obtained for another 30 seconds. A full rundown on using leasing is too much for this post, but it is not too much work to write code that can acquireleases on a blob, and if a lease cannot be obtained, do a spin wait until a lock can be obtained.