Zoo 105 Podcast: adding Azure Blob storage

In this last step of my application, I wanted to save the podcast episodes (the real mp3 files) in an Azure Blob storage. I didn't want to slow down my optimized Azure Function, so I decided to download the mp3 files in a separate function.
How to invoke this second function? The first idea was to invoke it automatically when saving in CosmosDB (there is a CosmosDB trigger), but this solution is not fault resistant (or at least: you need to implement fault resistance yourself).
Azure provides fault resistance out-of-the-box with queues, so I chose to use them. Azure storage is already there when creating a Function, so I needed only to create a function to create the queue (if not existing):
public static async Task<CloudQueue> GetAzureQueueAsync(IConfigurationRoot config)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(config["AzureWebJobsStorage"]);
CloudQueueClient queueClient = storageAccount.CreateCloudQueueClient();
CloudQueue queue = queueClient.GetQueueReference(queueName);
await queue.CreateIfNotExistsAsync();
return queue;
}
Once I have a queue, it's easy to add items:
public static async Task EnqueueItemAsync(CloudQueue queue, Podcast2Download episode)
{
string serializedObj = JsonConvert.SerializeObject(episode);
CloudQueueMessage message = new CloudQueueMessage(serializedObj);
await queue.AddMessageAsync(message);
}
Item retrieval and eventual re-enqueuing in case of errors is automatically managed by Azure; all you need is to write a Function that has the queue trigger:
[FunctionName("Download2Blob")]
public static async Task Run([QueueTrigger("podcast2download", Connection = "AzureWebJobsStorage")]
string myQueueItem, ILogger logger, Microsoft.Azure.WebJobs.ExecutionContext context)
{
logger.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
...
Podcast2Download episode2download = AzureQueueHelper.DeserializeItem(myQueueItem);
...
}
UPDATE: as described here, in Azure Functions v2 the QueueTriggerAttribute requires the NuGet package Microsoft.Azure.WebJobs.Extensions.Storage.
Finally, after having downloaded the file, to save it in an Azure Blob, you need to get a container:
public static CloudBlobContainer GetBlobContainer(IConfigurationRoot config)
{
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(config["AzureWebJobsStorage"]);
CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference(blobContainerName);
return cloudBlobContainer;
}
With the container, you can call multiple methods to manage its files. In my case, I wanted to only upload new files:
public static async Task<long> StoreFileAsync(CloudBlobContainer cloudBlobContainer, DateTime dateUtc, string fileName, Stream stream)
{
// If the container does't exist, create it
if (!await cloudBlobContainer.ExistsAsync())
try { await cloudBlobContainer.CreateAsync(); } catch { }
string pathFileName = ...;
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(pathFileName);
await cloudBlockBlob.UploadFromStreamAsync(stream);
return stream.Position;
}
The source code for this step is available in the dedicated GitHub repository, under the tag 3.AzureBlobStorage.