I have an Azure Function which is triggered whenever an image is uploaded to a container in a Azure storage account. I read the stream and do some actions.
I would also like to get the uri of the blob that is triggering the Function but how would I do this? Do I have to work with additional inputs / outputs?
public static void Run(Stream myBlob, TraceWriter log)
{
//get byte array of the stream
byte[] image = ReadStream(myBlob);
// ...
}
If you really want the full URI, as opposed to just the blob relative path (which string blobTrigger would give you), you'll need to do something like this:
public static void Run(CloudBlockBlob myBlob, TraceWriter log)
{
// ...
}
At that point, you can use the CloudBlockBlob object model both to get the URI (e.g. StorageUri or some other related props) and to get the stream (e.g. BeginDownloadToStream). Note that when you do this, you can no longer directly receive the Stream as an input param.
Based on the webjobs' documentation for blobs, you can simply add a string blobTrigger parameter:
public static void Run(Stream myBlob, string blobTrigger, TraceWriter log)
In your binding, you can define a variable name for blob path:
"path": "foo/{name}.bar",
Then add name as another function parameter:
public static void Run(Stream myBlob, string name, TraceWriter log)
Related
When configuring a blob trigger for an Azure function, it is possible to use pattern matching on the blob name to map portions of the name, to variables in the function. For example:
[FunctionName("BlobTriggered")]
public static void BlobTriggered(
[BlobTrigger("myContainer/{name}.{extension}")] Stream myBlob,
string name,
string extension,
TraceWriter log)
{
...
}
But what if I wanted one of the variables to be optional? For the given example, what if I wanted the extension to be optional? Thus, I would need to make the ., and the extension itself, to be optional. Is that possible to achieve?
You can remove the pattern and handle the filename and extension yourself like this.
public static void Run([BlobTrigger("test/{name}", Connection = "teststorage_STORAGE")]Stream myBlob, string name, ILogger log)
{
log.LogInformation($"Name: {Path.GetFileName(name)}");
log.LogInformation($"Extension: {Path.GetExtension(name)}");
}
Log entries below where the first upload is with extension and the last without.
This example shows how to set blob properties such as ContentType using C#. How can this be done in the following Azure Function? The method signature does not use a CloudBlob object, but rather a Stream object to read the blob.
[FunctionName("MyFunction")]
public static async Task Run([BlobTrigger("container-name/folder-name/{name}", Connection = "ConnectionString")]Stream myBlob, string name, ILogger log, Binder binder)
{
// How to change the ContentType property?
}
Please use the code below(I'm using visual studio 2017, and create function v2):
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Blob;
namespace FunctionApp3
{
public static class Function1
{
[FunctionName("Function1")]
public static void Run([BlobTrigger("container-name/folder-name/{name}", Connection = "AzureWebJobsStorage")]ICloudBlob myBlob, string name, ILogger log)
{
log.LogInformation("...change blob property...");
//specify the property here
myBlob.Properties.ContentType = "text/html";
//commit the property
myBlob.SetPropertiesAsync();
}
}
}
Assume the following typical Queue Trigger function:
public void Run([QueueTrigger("queue1")]object data, ILogger log)
{
// Do something with data
}
My problem is that "queue1" has to be a constant field, so it has to be defined at compile time.
Also, I'd want to have a base class for Queue Triggers, that could work like this:
public abstract class QueueBase<TModel>
{
public void Run([QueueTrigger("queueName")]TModel data, ILogger log)
{
// Do something with data, log something etc.
OnRunExecuted(data);
// Do something with data, log something etc.
}
public abstract void OnRunExecuted(TModel data);
}
with this, I could write own classes which inherit from QueueBase but can even live inside a library which doesn't have Microsoft.Azure.WebJobs dependency:
public class MyQueueHandler : QueueBase<MyModel>
{
public void OnRunExecuted(MyModel data) => ...;
}
But it's impossible to pass in a Queue name... is it?
See binding expressions:
In short, you can pass in a variable queue name as "%queue-name-variable%"
[FunctionName("QueueTrigger")]
public static void Run(
[QueueTrigger("%queue-name-variable%")]string myQueueItem,
ILogger log)
{
log.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
}
Where input-queue-name is defined in your configuration like
{"queue-name-variable": "queue-name-in-current-env"}
As i remember attribute QueueTrigger accept only const string, so you can try make some tricks using environment variables like in post how to pass dynamic queue name
Now i'm working at writing unit test on azure service bus trigger function
It's highly needed to mock somehow BrokeredMessage object that pass around into function. Function declaration is given below:
public static void Run(
[ServiceBusTrigger("saas01.queue.dbmigration", AccessRights.Manage, Connection = "connection")]BrokeredMessage message)
Unfortunately, i can't find any applicable way to mock it. It hardly mocking du to this class is sealed and i can't event create wrapper around it. Do you have some ideas about it?
Thanks for helping
,
One solution is to create a wrapper around BrokeredMessage you can test, as is done here. Here's also a MSDN post to the ServiceBus team that talks about using a wrapper too.
Note that Azure Functions V2 uses the Message class, which is public and not sealed.
[FunctionName("ServiceBusFunc")]
public static void Run([ServiceBusTrigger("myqueue", AccessRights.Manage, Connection = "ServiceBus")]BrokeredMessage myQueueItem, TraceWriter log)
{
var message = new MyBrokeredMessage(myQueueItem);
BusinessLogic(message, log);
}
public static void BusinessLogic(MyBrokeredMessage myMessage, TraceWriter log)
{
var stream = myMessage.GetBody<Stream>();
var reader = new StreamReader(stream);
log.Info($"C# ServiceBus queue trigger function processed message: '{reader.ReadToEnd() }'");
}
public class MyBrokeredMessage
{
private BrokeredMessage _msg;
public MyBrokeredMessage(BrokeredMessage msg) => _msg = msg;
public T GetBody<T>()
{
return _msg.GetBody<T>();
}
}
I am trying to move the events from event hub to the Blob storage. I have created trigger on the eventhub to trigger whenever a message comes to event hub. Also, i have configured outputs as Blob Storage. Right now, what i am doing within the function is:
public static void Run(string myEventHubMessage, out string outputBlob, TraceWriter log)
{
outputBlob = myEventHubMessage;
}
This will create a new blob in the container mentioned in the Ouputs configuration. But, I want is to create a blob with a specified name based on the data present in the eventhub message and need to set the content type and other metadata while saving the data to Azure Blob. Can someone help how to do this?
Regards,
John
You might no longer need to do that.
Event hub now supports piping to blob storage out of the box.
There are several possibilities to configure your output binding.
If you need to set the Blob path based on event hub message properties, you can declare your strongly typed message
public class MyEvent
{
public string SomeName { get; set; }
// more properties
}
then bind it declaratively to blob path, e.g.
{
"type": "blob",
"name": "outputBlob",
"path": "mycontainer/{SomeName}.json",
"connection": "...",
"direction": "out"
},
and modify the function accordingly
public static void Run(MyEvent myEventHubMessage, out MyEvent outputBlob)
{
outputBlob = myEventHubMessage;
}
If you need more advanced calculation to determine the output path, you can remove declarative output binding from function.json and use imperative binding:
public static async Task Run(string myEventHubMessage, Binder binder)
{
var path = ...;
using (var writer = binder.Bind<TextWriter>(new BlobAttribute(path)))
{
writer.Write(myEventHubMessage);
}
}
If you need to set more properties of Blob, bind to ICollector<CloudBlockBlob>
var collector = binder.Bind<ICollector<CloudBlockBlob>>(new BlobAttribute(path)));
collector.Add(new CloudBlockBlob { ... });
You should play with all those options to identify which scenario works for you.