In this last step of my application, I wanted to save the podcast episodes (the real mp3 files) in an Azure Blob storage. I did'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, TraceWriter log, Microsoft.Azure.WebJobs.ExecutionContext context)
{
	log.Info($"C# Queue trigger function processed: {myQueueItem}");

	...

	Podcast2Download episode2download = AzureQueueHelper.DeserializeItem(myQueueItem);

	...
}

 

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 AzureBlobStorage.