Getting work done in the cloud

TL;DR: Creating a Azure Function using some of the more complex types for binding is like finding a needle in the famous hay-stack. It seems like Microsoft have some work to do here, documenting the whole thing. At least it seems like they know it.


The problem

To get some background it would make sence reading the first post I did about Azure Functions. It's the Getting work done in the cloud - on time post.

I have a function pulling down all new products from some manufacturers, and adding these products to a growing catalogue on my site. When a product is added, either automatically by the timer triggered function, or manually by me or any other visitor, pictures for the product can also be added. These end up in blob storage on Azure. I need to create thumbnails for these pictures, so that we have a smaller picture to use in the product lists on the site.

Until now, this has, again, been handled by the site. Whenever a request came in for the thumbnail picture, if it did not exist, it would be generated on the first request. This has been a bit of a hit on performance, particularly because it could be several pictures that needed this operation, as the thumbnails are part of a list. So if several new products were added to a manufacturer's list, we needed to generate thumbnails for all within seconds. And then there's the issue with multiple requests to the same thumbnail before it has been requested.

The solution

Azure Functions can be triggered by a number of events. In my first post we looked at a timer trigger (kind of like good old cron). This time around we are going to use blob storage as the trigger. Whenever a new file is added to blob storage, in a certain container, my function will start running.

So this sounds simple right? And with the things I found out about functions for the first blog post, it was easy creating the new function, and triggering it. But...

I read somewhere that you can bind the parameters of the function to the blob and you can even provide a new blob as a parameter, so you don't have to fetch or create them yourself. That's neat! When I first tried this out, I ended up getting the blob serialized to a string and got it bound to the first parameter. Not exactly what I needed.

I fetch my functions from a repository on my BitBucket account, so I always start out with a clean slate. So that's what I did. After reading bits and pieces about the subject, from various sites, I had to give up. I ended up, again, posting my problem on StackOverflow. You can read my question on How to bind to ICloudBlob or some other (not string) type.

Honestly, and as Lindy Donna(?) from Microsoft kind of replies, the documentation on Azure Functions isn't really done yet! Or at least, it can be better!

So with the help of the hints in the reply, I actually got it working.

This time around I'm going to post all 3 files of my project. I'm guessing this is a senario lots of people have, making thumbnails of images in blob storage with a function.

If you have an external dependency, a nuget package, you should put it in your project.json file like so:

{
  "frameworks": {
    "net46": {
      "dependencies": {
        "ImageResizer": "4.0.5"
      }
    }
  }
}

I'm using the ImageResizer package for doing the actual heavy-lifting of creating the thumbnail. As you can also gather from this file, I'm using .NET 4.6 for my function. Remember to select this in the app settings in the Azure portal, as there are several different targets/version of .NET available.

The next file, the function.json file, binds the parameters etc.

{
  "bindings": [
    {
      "type": "blobTrigger",
      "name": "image",
      "path": "kitimages/{name}.{ext}",
      "connection": "Storage.KitImages",
      "direction": "in"
    },
    {
      "path": "thumbnails/{name}_300_200.{ext}",
      "type": "blob",
      "name": "imageOut",
      "direction": "InOut",
      "connection": "Storage.KitImages"
    }
  ],
  "disabled": false
}

Just a few words on this, as most of it will make more sense when you see the signature of the function method too. We're looking at the blob storage container named kitimages, and the connection string for the storage account, can be found in the appsetting with the key Storage.KitImages - so remember to create that appsetting in the Azure portal. Another important thing to notice, is the 'InOut' direction of the second binding. This will provide you with a fresh empty blob, where you can put the output.

Because I can, I'm using a "pattern match", for lack of a better expression. When a new blob is added, I'll get the name and extension of the blob, and I'll reuse the name and extension in the blob that will be created to contain the thumbnail. This is all done without any work done by me in my function. So why not?

Okay, finally the run.csx file:

#r "System.Drawing"
#r "Microsoft.WindowsAzure.Storage"

using ImageResizer;
using Microsoft.WindowsAzure.Storage.Blob;
using System;
using System.IO;

public static void Run(Stream image, String name, String ext, CloudBlockBlob imageOut, TraceWriter log)  // output blobs
{
    using (MemoryStream outStream = new MemoryStream()) {
        ImageBuilder.Current.Build(image, outStream, new ResizeSettings(300, 200, FitMode.Crop, null));
        outStream.Seek(0, SeekOrigin.Begin);

        imageOut.Metadata["MetaName"] = "meta";
        imageOut.UploadFromStream(outStream);
        imageOut.Properties.ContentType = "image/" + ext;
        imageOut.SetProperties();
}

    log.Info($"Thumbnailed {name}.{ext}");
}

Let's go into detail with this one.

In the first two lines I'm referencing 2 assemblies, the System.Drawing and Microsoft.WindowsAzure.Storage assemblies. The first one is needed because the ImageResizer package depends on it, the second one is needed because I'm binding to a CloudBlockBlob parameter.

So back to the signature of the method. If you look at the function.json file again, you'll notice the name of the first binding, image is here again, bound to a Stream. ImageResizer works on streams, so no need to bind to a CloudBlockBlob, but you could. Then we have the name and ext parameters, again from the binding. I use these for debug information, and to create the content type of the new blob. And then we have the CloudBlockBlob imageOut, if you write anything to this blob, it will end up, as specified by the binding in the function.json file, as a new blob in the thumbnails container, with the name given in the binding.

Work done!

This is exactly what I was looking for when I decided to go the function way with this kind of task/workload. A simple method handling a simple problem.

Comments

Thank you for your comment! If it's not spam, it will be published shortly!

Your e-mail address will not be published. Required fields are market *

*

Copyright © 2015-2016 by Steen F. Tøttrup. Powered by NBlogEngine by creativeminds & Romangie Theme.