2

I'm trying to bind to a blob output in an Async method following this post: How can I bind output values to my async Azure Function?

I have multiple output bindings so just returning is not an option

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, IAsyncCollector<string> collection, TraceWriter log)
{
    if (req.Method == HttpMethod.Post) 
    {
        string jsonContent = await req.Content.ReadAsStringAsync();

        // Save to blob 
        await collection.AddAsync(jsonContent);

        return req.CreateResponse(HttpStatusCode.OK);
    }
    else 
    {
        return req.CreateResponse(HttpStatusCode.BadRequest);

    }
}

My binding for the blob is :

{
  "bindings": [
    {
      "authLevel": "function",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in"
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    },
    {
      "type": "blob",
      "name": "collection",
      "path": "testdata/{rand-guid}.txt",
      "connection": "test_STORAGE",
      "direction": "out"
    }
  ],
  "disabled": false
}

But whenever I do this I get the following:

Error: Function ($WebHook) Error: Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.WebHook'. Microsoft.Azure.WebJobs.Host: Can't bind Blob to type 'Microsoft.Azure.WebJobs.IAsyncCollector`1[System.String]'

nastassiar
  • 1,545
  • 2
  • 24
  • 43
  • Collectors are not supported for Blob output bindings, see [this issue](https://github.com/Azure/Azure-Functions/issues/162). Do you need to output 1 or many items? – Mikhail Shilkov Jun 21 '17 at 09:07
  • Just one item. But I need to do it in an asynchronous method and the return value needs to be the http response. – nastassiar Jun 21 '17 at 09:09

2 Answers2

7

Collectors are not supported for Blob output bindings, see this issue.

For variable amount of output blobs (0 or 1 in your case, but can be any), you would have to use imperative bindings. Remove collection binding from your function.json and then do this:

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, Binder binder)
{
    if (req.Method == HttpMethod.Post) 
    {
        string jsonContent = await req.Content.ReadAsStringAsync();

        var attributes = new Attribute[]
        {    
            new BlobAttribute("testdata/{rand-guid}.txt"),
            new StorageAccountAttribute("test_STORAGE")
        };

        using (var writer = await binder.BindAsync<TextWriter>(attributes))
        {
            writer.Write(jsonContent);
        }

        return req.CreateResponse(HttpStatusCode.OK);
    }
    else 
    {
        return req.CreateResponse(HttpStatusCode.BadRequest);    
    }
}
Mikhail Shilkov
  • 34,128
  • 3
  • 68
  • 107
3

You can use the Blob-Binding.

I preferred this way, because I'm able to specify the ContentType.

 [FunctionName(nameof(Store))]
    public static async Task<IActionResult> Store(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
        [Blob(
            "firstcontainer",
            FileAccess.Read,
            Connection = "blobConnection")] CloudBlobContainer blobContainer,
        ILogger log)
    {
        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

        string filename = "nextlevel/body.json";

        CloudBlockBlob blob = blobContainer.GetBlockBlobReference($"{filename}");
        blob.Properties.ContentType = "application/json";
        await blob.UploadTextAsync(requestBody);

        return (ActionResult)new OkResult();
    }
Markus Meyer
  • 3,327
  • 10
  • 22
  • 35