Azure Functions – Resize Images Uploaded to Blob Storage

Scenario

I have been capturing images with my power App, which are uploaded to a Blob Storage. My power App uses the build in phone camera to capture images, hence each image size is more than 2 MB.

I use these images in one of my reports generated periodically via logic app (also converted to pdf). Sometimes there can be more than 40 images in my report and as you can imagine the system takes a lot of time to first download the images, because of the size, and my report generation fails.

Now, to avoid this situation, I can do couple of things:

  • Setup my camera to use lowest settings for image size. However, with today’s devices even the lowest settings generate images in MBs. Also, it is not possible all the time. Try setting that as best practice for your customers!
  • I can use the Camera App (provided by the Power App), which usually captures images in KBs. However, there are various limitations like, I can’t use (at least for now) Flash, zoom in, zoom out and many other features that my device provides.

Solution

So, the most appropriate solution would be to create a function that monitors my Blob Storage, where my images are uploaded and whenever there is an image which exceeds certain size, I would like the function to resize the image and store it in the same place.

Credits to original post.

Prerequisite

To replicate this scenario, we will create a Blob Storage to store our images and use Microsoft Azure Storage Explorer.

Blob Storage

Create a Blob Storage:

  • Specify a unique Name
  • Select Account Kind
  • Select Location
  • Select Replication
  • Select Subscription
  • Create a new Resource Group or use existing
  • Click Create.

Within the storage, use the Blob Service and create a Container where the images will be stored.

Microsoft Azure Storage Explorer

Download and Install Microsoft Azure Storage Explorer and configure it to access the Blob Storage. We will be using it to simulate upload of sample image.

Create a Function App Service

  • Use Function App to create a new function:
    • Specify unique App Name
    • Select Subscription
    • Create a new Resource Group or use existing
    • Select Hosting Plan
    • Select Location
    • Create a new Storage or use existing
    • Click Create

Create a Function Trigger

  • Create a new function trigger based on Blob Trigger template using C#
  • Specify Name of the function trigger
  • Specify the Path, this is the path to the source of the images
  • Select the Storage Account Connection
  • Click Create

NOTE: If you do not see your storage account connection, click New.

The portal will create a binding for your script that will allow you to process files created at the path specified.

The connection string for your storage account will automatically be created and added to your Function App. To see how this is connected, inspect the function.json file in your function through the View Files tool pane.

You can also click on the Integrate menu and check the settings:

Add New Output Binding

We need to add another parameter binding, this time for output, so that we can save out the resized image back to Azure Blob Storage (same location in this example).

  • Click on the Integrate menu
  • Click New Output to create output binding.

  • Select Azure Blob Storage

  • Specify Blob Parameter Name
  • Specify Path (in this example it is the same path where we need to save the output)
  • Select Storage Account Connection

Note: The path, where we are reusing the {imageName} parameter. This was the name of the file coming into our function, and we will save it out as the same filename in the same location, essentially replacing the original file. If you want you can specify different path to store the resized file.

Add Files to Make the Function Work

Project.json

  • Under View Files button add a new file called project.json and add the following code. This is how the Function Apps use to restore packages from NuGet.
{

“frameworks”: {

“net46”:{

“dependencies”: {

“ImageResizer”: “4.0.4”

}

}

}

}

  • Save the file.

Run.csx

  • Navigate to run.csx, the script file for the endpoint.
  • Use the following code. We are using the ImageResizer library to execute a resize operation with one stream as the source and the other as the output.
#r “System.Drawing”

using ImageResizer;

using System.Drawing;

using System.Drawing.Imaging;

public static void Run(Stream inputImage, string imageName, Stream resizedImage, TraceWriter log)

{

log.Info($”C# Blob trigger function Processed blob\n Name:{imageName} \n Size: {inputImage.Length} Bytes”);

var settings = new ImageResizer.ResizeSettings{

MaxWidth = 400

};

if(inputImage.Length > 500000){

ImageResizer.ImageBuilder.Current.Build(inputImage, resizedImage, settings);

}

else

{

log.Info($”Name:{imageName} \n Size: {inputImage.Length} Bytes is too small ignoring file”);

}

}

  • Save the file.

Check the log to see if the compilation was a success.

Test the Solution

  • In this example we are uploading an image file of size around 3.71 MB.

  • Using the Azure Storage Explorer upload the sample image. Use the Upload button. Note the size.

  • The function sees that an image of larger size has been uploaded and runs the logic to resize the image.
  • Refresh the Azure Storage Explorer after few seconds and check the size of the image, it would have been reduced as per the logic.

Using Azure Functions to Call Dynamics 365.

Credits…Original Post by Nishant Rana (Big Thanks!).

Here is an example of a simple Azure Function that refers our CRM assemblies and creates contact record in CRM.

Log in to Azure Portal, and perform the following actions:

  • Optionally, Create a Resource Group, so that it is easy to maintain the resources.
  • Search for “Function App” and create one. NOTE: The App Name of the Function App should be a globally unique name.

  • Add (+) a new function (GenericWebHook-Csharp template)

  • This creates a new function (“CallCRMApps” in this example).
  • Select the function, and under View Files (Right end of your screen), add a new file and give it a name, for example “project.json”. NOTE: It is within this file we will refer our Nuget Packages that we need in our function.

  • Click on project.json and write the following code:

NOTE: At the time of writing this article the latest version of Nuget was “9.0.0.7”.

PROJECT.JSON

{

“frameworks”: {

“net46”:{

“dependencies”: {

“Microsoft.CrmSdk.CoreAssemblies”: “9.0.0.7”

}

}

}

}

  • Click Save and Run and notice the Log (bottom of the screen).

  • Now click on run.csx file. Write the following sample code for the Azure Function. Please replace the highlighted Organization Serve with your D365 Organization Service Endpoint Address, also the username and password for your D365 environment.

RUN.CSX

using System.Net;

using Microsoft.Xrm.Sdk;

using Microsoft.Xrm.Sdk.Client;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)

{

log.Info(“C# HTTP trigger function processed a request.”);

// parse query parameter

string firstname = req.GetQueryNameValuePairs()

.FirstOrDefault(q => string.Compare(q.Key, “firstname”, true) == 0)

.Value;

string lastname = req.GetQueryNameValuePairs()

.FirstOrDefault(q => string.Compare(q.Key, “lastname”, true) == 0)

.Value;

IServiceManagement<IOrganizationService> orgServiceManagement = ServiceConfigurationFactory.CreateManagement<IOrganizationService>(new Uri(“<your organization service>“));

AuthenticationCredentials authCredentials = new AuthenticationCredentials();

authCredentials.ClientCredentials.UserName.UserName = “<username>“;

authCredentials.ClientCredentials.UserName.Password = “<password>“;

AuthenticationCredentials tokenCredentials = orgServiceManagement.Authenticate(authCredentials);

OrganizationServiceProxy organizationProxy = new OrganizationServiceProxy(orgServiceManagement, tokenCredentials.SecurityTokenResponse);

Entity contact = new Entity(“contact”);

contact.Attributes[“firstname”] = firstname;

contact.Attributes[“lastname”] = lastname;

var contactId = organizationProxy.Create(contact);

// Get request body

dynamic data = await req.Content.ReadAsAsync<object>();

string fullname = “”;

return fullname == null

? req.CreateResponse(HttpStatusCode.BadRequest, “Please pass a name on the query string or in the request body”)

: req.CreateResponse(HttpStatusCode.OK, “Contact created in CRM ” + contactId.ToString());

}

NOTE: To get your organization service , in your D365 environment click, Settings, Customizations, Developer Resources and copy the Endpoint Address under Organization Service.

Test Function

Add a query under Test tab to enter the First Name and Last Name (click + Add parameter) and click Run.

If your query is OK, you will get a Status: 200 OK and the output stating that a new Contact is created in D365.

Check the D365 Contact and search for the contact.