Dynamic output file name of ApiHubFile Azure Funct

2019-02-27 14:36发布

问题:

I have an Azure Function with Timer Trigger, and then I want to generate a file with dynamic (defined in runtime) name and contents and save it to e.g. OneDrive.

My function code:

public static void Run(TimerInfo myTimer, out string filename, out string content)
{
    filename = $"{DateTime.Now}.txt";
    content = $"Generated at {DateTime.Now} by Azure Functions";    
}

And function.json:

{
  "bindings": [
    {
      "name": "myTimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "0 */5 * * * *"
    },
    {
      "type": "apiHubFile",
      "name": "content",
      "path": "{filename}",
      "connection": "onedrive_ONEDRIVE",
      "direction": "out"
    }
  ],
  "disabled": false
}

This fails though, with

Error indexing method 'Functions.TimerTriggerCSharp1'. Microsoft.Azure.WebJobs.Host: 
Cannot bind parameter 'filename' to type String&. Make sure the parameter Type 
is supported by the binding. If you're using binding extensions 
(e.g. ServiceBus, Timers, etc.) make sure you've called the registration method 
for the extension(s) in your startup code (e.g. config.UseServiceBus(), 
config.UseTimers(), etc.).

回答1:

Here is how you could do it:

#r "Microsoft.Azure.WebJobs.Extensions.ApiHub"

using System;
using System.IO;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host.Bindings.Runtime;

public static async Task Run(TimerInfo myTimer, TraceWriter log, Binder binder)
{
    log.Info($"C# Timer trigger function executed at: {DateTime.Now}");    

    var fileName = "mypath/" + DateTime.Now.ToString("yyyy-MM-ddThh-mm-ss") + ".txt";

    var attributes = new Attribute[]
    {    
        new ApiHubFileAttribute("onedrive_ONEDRIVE", fileName, FileAccess.Write)
    };


    var writer = await binder.BindAsync<TextWriter>(attributes);
    var content = $"Generated at {DateTime.Now} by Azure Functions"; 

    writer.Write(content);
}

And the function.json file:

    {
  "bindings": [
    {
      "name": "myTimer",
      "type": "timerTrigger",
      "direction": "in",
      "schedule": "10 * * * * *"
    },
    {
      "type": "apiHubFile",
      "name": "outputFile",
      "connection": "onedrive_ONEDRIVE",
      "direction": "out"
    }
  ],
  "disabled": false
}

You shouldn't really need the apiHubFile declaration in your function.json but because of a bug I noticed today it should still be there. we will fix that bug.



回答2:

To have full control over the name and path of an output during function execution, you need to use imperative binding

For example: function.json

{
      "type": "blob",
      "name": "outputBinder",
      "path": "export/test",
      "connection": "AzureWebJobsStorage",
      "direction": "out"
},

Function:

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log, IBinder outputBinder)
{
     var attribute new BlobAttribute($"{some dynamic path}/{some dynamic filename}", FileAccess.Write);
     using (var stream = await outputBinder.BindAsync<Stream>(attribute))
     {
          // do whatever you want with this stream here...
     }
}