模式匹配Blob名称与具有输入绑定的函数变量

时间:2018-10-12 02:58:12

标签: c# azure-functions azure-storage-blobs

根据Azure Blob storage bindings for Azure Functions documentation,在配置Blob触发器时,您可以使用Blob名称上的模式匹配将路径的一部分映射到函数中的变量,例如。

[FunctionName("BlobTriggered")]        
public static void BlobTriggered(
    [BlobTrigger("myContainer/{name}.{extension}")] Stream myBlob,
    string name,
    string extension,
    TraceWriter log)
{
    // Given the blob path "myContainer/myBlob.png":
    // name == "myBlob"
    // extension == "png"
}

我已经对此进行了测试,它在我的用例中表现出色,但是由于BlobTrigger触发的大量延迟(通常超过5分钟),因此不是一个可行的选择。结果,我正在根据Azure Functions scale and hosting documentation的建议,将其设置为事件网格触发器:

  

在“消费”计划上使用Blob触发器时,处理新Blob最多可能会延迟10分钟。功能应用程序闲置时会发生此延迟。功能应用程序运行后,将立即处理Blob。为了避免这种冷启动延迟,请使用启用了始终启用的App Service计划,或使用事件网格触发器。

有什么方法可以从输入绑定而不是触发器中获得相同的模式匹配行为吗?

在我的特定情况下,我为创建Blob建立了一个EventGrid订阅,该订阅运行一个Orchestrator函数,该Orchestrator函数调用一个活动函数来读取和解析Blob:

[FunctionName("NewBlobCreated")]
public static async Task NewBlobCreated(
    [EventGridTrigger]EventGridEvent eventGridEvent,
    [OrchestrationClient]DurableOrchestrationClient starter,
    ILogger log)
{
    // Start our orchestrator function to read the file
    string instanceId = await starter.StartNewAsync(
        "OrchestrateBlobReader",
        eventGridEvent);
}

// Orchestrator function
[FunctionName("OrchestrateBlobReader")]
public static async Task OrchestrateBlobReader(
    [OrchestrationTrigger] DurableOrchestrationContext context,
    ILogger log)
{
    var eventGridEvent = context.GetInput<EventGridEvent>();
    var parsedBlob = await context.CallActivityAsync<string>("ReadBlob", eventGridEvent.Data);        
    ...
}

[FunctionName("ReadBlob")]
public static async Task<string> ReadBlob(
    [ActivityTrigger] JObject eventData,
    [Blob("{data.url}", FileAccess.Read)]CloudBlockBlob blob,
    ILogger log)
{
    using (var blobStream = await blob.OpenReadAsync())
    {
        // Blob is able to be read from blobStream here
        ...
    }
}

理想情况下,我希望我的ReadBlob函数的行为类似于上述第一个示例中的BlobTriggered函数,以执行以下操作:

[FunctionName("ReadBlob")]
public static async Task<string> ReadBlob(
    [ActivityTrigger] JObject eventData,
    [Blob("{data.url}", FileAccess.Read)]CloudBlockBlob blob,
    string extension,
    ILogger log)
{
    if (extension.Equals("txt", StringComparison.OrdinalIgnoreCase))
    { ... }
    else if (extension.Equals("png", StringComparison.OrdinalIgnoreCase)
    { ... }
    else
    { ... }
}

问题是我看不到将extension参数绑定到Blob输入绑定的任何方法,特别是将路径绑定到了BlobTrigger EventGridEvent提供的网址 以eventData JObject的形式。

在这种情况下是否可以实现相同的模式匹配功能?还是我必须自己解析路径字符串以提取相关信息?

1 个答案:

答案 0 :(得分:3)

在查看了Blob触发器绑定的源代码之后,我的“快速而肮脏”的解决方案是利用底层BindingTemplateSource class,触发器将其用于将路径和模式映射到字典。 / p>

更新后的ReadBlob函数如下:

// So we can access the BindingTemplateSource class
using Microsoft.Azure.WebJobs.Host.Bindings.Path;

[FunctionName("ReadBlob")]
public static async Task<string> ReadBlob(
    [ActivityTrigger] JObject eventData,
    [Blob("{data.url}", FileAccess.Read)]CloudBlockBlob blob,
    ILogger log)
{
    // Define the pattern to match
    var blobPattern = "myContainer/{name}.{extension}";
    // Create a BindingTemplateSource from the pattern string
    var patternTemplate = BindingTemplateSource.FromString(blobPattern);
    // Use this BindingTemplateSource to create the binding data
    // This returns a IReadOnlyDictionary<string, object> with the parameters mapped
    var parameters = patternTemplate.CreateBindingData($"{blob.Container.Name}/{blob.Name}");
    // Assuming blob path was "myContainer/myBlob.png":
    // Parameters are objects so we need to ToString() them
    var name = parameters["name"].ToString(); // name == "myBlob"
    var extension = parameters["extension"].ToString(); // extension == "png"

    if (extension.Equals("txt", StringComparison.OrdinalIgnoreCase))
    { ... }
    else if (extension.Equals("png", StringComparison.OrdinalIgnoreCase))
    { 
        // This executes now!
    }
    else
    { ... }
}

然后可以将此功能包装在custom binding中,在其中将参数映射到函数上的输出绑定,就像BlobTrigger所做的那样,这是最优雅的解决方案,但是像这样将其侵入函数中短期内达到我的需求