Azure Functions Triggers and Bindings - Part 2 - Blob storage

2023, Feb 14

This is part 2 of the Azure Functions Triggers and Bindings series where Triggers and Bindings are explored with C# examples. In this post, Azure Blob storage is explored with In-Process and Isolated Functions.

Triggers and Bindings

There are 3 use cases that we can take advantage of when dealing with Azure Blob storage on Azure Functions:

  • Listen to blobs with BlobTrigger, applied to both In-Process and Isolated models.
  • Read blobs from the storage with Blob and BlobInput, an input binding applied to In-Process and Isolated models respectively.
  • Save blobs to the storage with Blob and BlobOutput, an output binding applied to In-Process and Isolated models respectively.

Both triggers and bindings are explored below with In-Process and Isolated model examples.

They have similar implementations, except in Isolated mode, where bindings cannot handle Streams but only strings.

Triggers

A single trigger BlobTrigger is available in both implementations.

In-Process Triggers

The In-Process model leverages the NuGet package Microsoft.Azure.WebJobs.Extensions.Storage, version 5.0.1 (currently).

Trigger.cs

public class Trigger
{
    [FunctionName(nameof(BlobTrigger))]
    public void BlobTrigger([BlobTrigger("marketing/{name}")]Stream myBlob, string name, ILogger log)
    {
        log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
    }
}

Isolated Triggers

The Isolated model leverages the NuGet package Microsoft.Azure.Functions.Worker.Extensions.Storage, version 5.0.1 (currently).

Trigger.cs

public class Trigger
{
    private readonly ILogger _logger;

    public Trigger(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<Trigger>();
    }

    [Function(nameof(BlobTrigger))]
    public void BlobTrigger([BlobTrigger("marketing/{name}", Connection = "AzureWebJobsStorage")] string myBlob, string name)
    {
        _logger.LogInformation($"C# Blob trigger function Processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
    }
}

Testing Triggers

To validate the triggers, either start the In-process or Isolated model, then add a file to the blob container marketing by using Azure Storage Explorer.

Storage Explorer

  1. Create the blob container marketing
  2. Click to upload a file
  3. Select Upload Files
  4. Click to select a file
  5. Upload it

This should trigger the BlobTrigger function.

Bindings

There are 2 types of bindings, the Input used for reading blobs and the Output used for writing blobs.

In all examples below, there are different blob name patterns1 to make a trigger match.

Input In-Process Binding

Input.cs

public class Input
{
    [FunctionName(nameof(BlobInput))]
    public void BlobInput(
        [BlobTrigger("marketing-txt/{name}.txt")] Stream blobTrigger, string name,
        [Blob("marketing-txt/{name}.txt", FileAccess.Read)] Stream blobContent, ILogger log)
    {
        log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name}.txt \n Size: {blobTrigger.Length} Bytes");

        // READ stream just uploaded
        using var reader = new StreamReader(blobContent);
        var data = reader.ReadToEnd();
        log.LogInformation($"{data}");
    }
}

The implementation above gets triggered when a .txt file is uploaded to marketing-txt container, then with the Input binding it reads [Blob("marketing-txt/{name}.txt", FileAccess.Read)] the Stream just uploaded.

Input Isolated Binding

Input.cs

public class Input
{
    private readonly ILogger _logger;

    public Input(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<Input>();
    }

    [Function(nameof(BlobInput))]
    public void BlobInput(
        [BlobTrigger("marketing-txt/{name}.txt", Connection = "AzureWebJobsStorage")] string blobTrigger, string name,
        [BlobInput("marketing-txt/{name}.txt", Connection = "AzureWebJobsStorage")] string blobContent
        )
    {
        _logger.LogInformation($"C# Blob trigger function Processed blob\n Name:{name}.txt \n Size: {blobTrigger.Length} Bytes");

        _logger.LogInformation($"{blobContent}");
    }
}

The implementation above gets triggered when a .txt file is uploaded to marketing-txt container, then with the Input binding it reads [BlobInput("marketing-txt/{name}.txt", Connection = "AzureWebJobsStorage")] the string just uploaded.

Output In-Process Binding

Output.cs

public class Output
{
    [FunctionName(nameof(BlobOutput))]
    public void BlobOutput(
        [BlobTrigger("marketing-txt/{name}.csv")] Stream blobTrigger, string name,
        [Blob("marketing-csv/{name}.csv", FileAccess.Write)] Stream blobContent, ILogger log)
    {
        log.LogInformation($"C# Blob trigger function Processed blob\n Name:{name}.csv \n Size: {blobTrigger.Length} Bytes");

        // DELETE csv from marketing-txt container
        var storageConnectionString = System.Environment.GetEnvironmentVariable("AzureWebJobsStorage");
        BlobClient client = new BlobClient(storageConnectionString, "marketing-txt", $"{name}.csv");
        client.DeleteIfExists();
    }
}

The implementation above gets triggered when a .csv file is uploaded erroneously to marketing-txt container, then with the Output binding it writes [Blob("marketing-csv/{name}.csv", FileAccess.Write)] the Stream to another container marketing-csv, and within the implementation, the .csv file is deleted from marketing-txt.

Output Isolated Binding

Output.cs

public class Output
{
    private readonly ILogger _logger;

    public Output(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<Output>();
    }

    [Function(nameof(BlobOutput))]
    [BlobOutput("marketing-csv/{name}.csv", Connection = "AzureWebJobsStorage")]
    public string BlobOutput([BlobTrigger("marketing-txt/{name}.csv", Connection = "AzureWebJobsStorage")] string blobTrigger, string name)
    {
        _logger.LogInformation($"C# Blob trigger function Processed blob\n Name:{name}.csv \n Size: {blobTrigger.Length} Bytes");

        // DELETE csv from marketing-txt container
        var storageConnectionString = System.Environment.GetEnvironmentVariable("AzureWebJobsStorage");
        BlobClient client = new BlobClient(storageConnectionString, "marketing-txt", $"{name}.csv");
        client.DeleteIfExists();

        return blobTrigger;
    }
}

The implementation above gets triggered when a .csv file is uploaded erroneously to marketing-txt container, then with the Output binding it writes [BlobOutput("marketing-csv/{name}.csv", Connection = "AzureWebJobsStorage")] the string to another container marketing-csv, and within the implementation, the .csv file is deleted from marketing-txt.

Testing Bindings

To validate the Input binding, create the container marketing-txt and then upload a .txt file to it. In this case, the function will print out the body of the .txt file uploaded.

Last, but not least, to validate the Output binding, create the container marketing-csv and then upload a .csv file to the marketing-txt container, as we are trying to check how the function is going to move files automatically.

The examples are available on PlayGoKids repository.

More posts will come to exemplify other Triggers and Bindings available in Azure Functions. Stay tuned.


  1. References: blob name patterns