Building an AWS Integration for Salesforce Image Handling
Tags: aws, webdev
Published: 2021-12-15 15:07:23

## Introduction

In this article, we’ll look at a solution that not only offloads your Salesforce image storage to Amazon Web Services (AWS), but also resizes and renders those images on demand. By offloading image storage to other solutions, your organization can free up Salesforce storage capacity.

We’ll build off an example use case from Salesforce Field Service, but there are many other scenarios that can take advantage of these techniques. Having both a Salesforce org with Field Service installed and an AWS account are prerequisites for this article.


## Use case

For our example, we will look at a fictional property maintenance company, Acme Maintenance. Acme manages property safety inspections on behalf of its clients. On-site inspectors use Salesforce Field Service to record their findings on their visits. 

As part of their site visits, inspectors take a lot of photographs on their mobile devices, and these are saved back to Salesforce. Acme would like to use these images in the Service Reports that they provide to their clients.

Acme has discovered that with the number of site visits, multiplied by the number of images per visit and the size of those images (and on top of that they want to see reasonably-sized thumbnails of those images in Salesforce and surface them on Service Reports), they’re reaching the initial default file storage limits on Salesforce. While they could easily purchase more file storage within Salesforce, their Salesforce architect has suggested that off-platform storage may be more optimal.

To solve this, we will look at interfacing with AWS for both image storage and image processing (resizing, cropping, orientation, and so on).



## AWS Services

For our use case, we will look at one of the AWS reference architectures, the [Serverless Image Handler](https://aws.amazon.com/solutions/implementations/serverless-image-handler/), and take advantage of [AWS CloudFormation](https://aws.amazon.com/cloudformation/) to deploy it. In some ways, you can liken CloudFormation to Salesforce metadata—a way to deploy your resources in a declarative, script-based manner.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdrap399fpufkzjyz6mk.png)

The [AWS documentation](http://docs.aws.amazon.com/solutions/latest/serverless-image-handler/welcome.html) goes into greater detail, but in summary, we will leverage the following components:



* **Cloudfront**: load balancing the requests into the API
* **API Gateway**: publishing the external API for use
* **Lambda**: the serverless application that handles the image processing
* **S3**: storage of the original uploaded images and log files
* **Secrets Manager** (optional): If you use the image URL signature feature, then you can retrieve secrets for digital signing from here.
* **Rekognition**: if you require content moderation, smart cropping, facial recognition, and so on.


### AWS configuration

By using a CloudFront template, we don’t need to know a great deal about each of these AWS technologies; we simply have to go through a few configuration steps. Please note that in this example, we will take default settings rather than enable advanced features. It’s recommended that you read through the [implementation guide](https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/welcome.html).


#### Create an S3 bucket for your images

The first task is creating an S3 bucket to store our original image files. Choose a bucket name that is unique and lowercase, and then pick the region that is geographically closest to you. Check the box to “Block all public access.”


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x991abkjm11xva1i895e.png)


#### Deploying the CloudFormation stack

From the [Serverless Image Handler main page](https://aws.amazon.com/solutions/implementations/serverless-image-handler/), click on **Launch in the AWS Console** to start deploying your own instance.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09bm3uq63r5jtx7vgj5p.png)

Before you start populating fields, be sure to select your AWS region in the top right corner of the screen on the CloudFormation page. Select the same region as your S3 bucket from the previous step.

The path to the CloudFormation template should be pre-populated. Click **Next**.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uk2s6jm5nlisahaagrdk.png)

On the next screen, we specify a name for our stack and provide the name of our S3 bucket. Select “No” for the “Demo UI” and then click **Next** again. On the “Configure stack options” page, accept the default and click **Next** one more time.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/70gfktzp7l1524f7814d.png)

Finally, on the review page, click the **Create Stack** button. As you refresh the page, you’ll see the progress of your stack build, which can take a few minutes. Then, you’ll see this:


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hbuf2egj1zdoaftx93ph.png)

As a final step, we need to go to the **Output** tab of the build and copy the value of the <code>ApiEndpoint<strong> </strong></code>parameter, as we’ll be using this in our Salesforce build.


## Salesforce build


### Custom Objects and fields

In Salesforce, we’ll start by creating a new custom object called **Work Order Attached Image**, which will hold the information for any image attachments that relate to a Work Order. The key fields to note are **Image RTF**, **Original Image URL**, and **Resized Image URL**. When asked whether to add a related list to the Work Order page layout, be sure to select it. 


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9ma5pudqge1cemjrx8t.png)

### Custom metadata

Next, we need to create a Custom Metadata type to store our configuration settings. In Setup, search for **Custom Metadata** and create a new type called **AWS Settings** with the custom fields shown below:


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0g6u0qe8lr23ke34d1eo.png)


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2f0d02z5cj47f0ouee5a.png)

Once created, click the **Manage AWS Settings** button and add the following three settings and save them.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q061xhabpmnbyp85snzs.png)

For the image handler endpoint value, use the `ApiEndpoint `value from AWS CloudFormation.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3lg7lfamt6a3hlxopr01.png)


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sp21dfx6v2aqnitogeab.png)



### Named credentials

Our next configuration item is for securely handling the access to AWS from Salesforce. In Setup, search for **Named Credentials** and create the following two credentials, substituting in your own AWS URL, region, and access key/secret from your AWS user. Be sure the **Name** fields are as shown in the screenshots, as this is used later in the code.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a9aaww5x9bt29lkuyqcl.png)



![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dxs67ku6k0m3p87mvnf7.png)


### Remote site settings

In order for the images from AWS to appear in our Service Reports, we need to add the API endpoint as a trusted domain via **Remote Site Settings.**


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gxg554xthcu8cftxn1b.png)

### Triggers and classes

Revisiting our scenario, image attachments from Field Service populate Salesforce as `ContentDocument` objects and associated supporting objects. Their object model looks like this:


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6j603nuetzj92zy0w9hq.png)

In order to capture newly attached images in the model, we can simply add a new Apex trigger to the `ContentDocumentLink` object. This will allow us to reference both the attachment and the object it is attached to.


#### ContentDocumentLinkTrigger and ContentDocumentLinkTriggerHandler

We build a <code>ContentDocumentLinkTrigger</code></strong> ([view code on GitHub](https://bit.ly/acmedemo-01)) that hands off processing to an Apex class for handling the trigger.

Our trigger handler, <code>ContentDocumentLinkTriggerHandler</code></strong> ([view code on GitHub](https://bit.ly/acmedemo-02)), takes the list of new <code>ContentDocumentLinks</code> and collates them into a <code>Map</code>, keyed by the linked entity ID. It then inspects them to see the type of the linked entity, and—if it is a Work Order—adds them to a <code>Set</code> for processing.

This processing consists of building further collections to map an entity to associated `ContentDocument` records, before handing this data off to a Batch Apex class for processing asynchronously.


#### WorkOrderAttachmentBatch

The batch class for Work Order attachments, <code>WorkOrderAttachmentBatch</code></strong> ([view code on GitHub](https://bit.ly/acmedemo-03)), iterates through the supplied collections, building up new <code>WorkOrderAttachedImage</code> records from the image filename and base URL of the AWS S3 bucket.

A call is made to the `ImageRotationHandler` class to get the crafted URL with which to call the Image API. Then, it constructs an RTF field with some simple HTML for rendering the resulting image.

Once the records have been created and saved, the list of image attachments passes to another batch class for the upload process.


#### ImageRotationHandler

The <code>ImageRotationHandler</code></strong> ([view code on GitHub](https://bit.ly/acmedemo-04)) is a small class that handles the construction of a valid request to the Image API handler. It constructs the required JSON structure from the parameters, serializes it to a string, encodes it as Base64, and then appends this to the URL of the image handler endpoint. Formatting requests is included in the AWS template documentation under the [Deployment section](https://docs.aws.amazon.com/solutions/latest/serverless-image-handler/deployment.html).


#### S3UploadHandler

The final batch class, <code>S3UploadHandler</code></strong> ([view code on GitHub](https://bit.ly/acmedemo-05)), handles the uploading of the attached images to our S3 bucket via a simple REST API call.


### Service Reports

Now that we have our images on S3 and our custom object populated with our links to the images, we can now return to our Field Service use case. In our Salesforce setup, search for and click on **Service Report Templates**. For demo purposes, we’ll create a new template.

On the template page, select **Work Order** from the Related Templates dropdown list. In the Body area of the template, add a new **List** section element, based on the Work Order Attached Image object, as shown below:


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zmgctie1lvwgh4s7hfeh.png)

Save the template and activate it.

We’re now ready for testing. Create a new Work Order in Salesforce and save it. Then, upload an image file (in JPG or PNG format) to the Files section. Once the image has been uploaded, refresh the page. You should then see a new record in the **Work Order Attached Images** related list. Clicking on the record should show a record similar to this, with the S3 image displayed:


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ni8doyav23r87xk1h3xj.png)

From the Work Order, click the **Create Service Report** button. (It may be in the overflow menu dropdown):


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nc16a394ev73frn5w5zn.png)

Select the Service Report template we created earlier and click the **Create PDF** button. A preview of the Service Report PDF should render, complete with a thumbnail of your uploaded image in the appropriate section.


![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1irowus3q7fexcubbaqk.png)

## Conclusion

In this article, we have successfully deployed a complete image handler solution to AWS, using a reference implementation from the AWS Solutions Library. We have implemented code with supporting objects and configuration in Salesforce to transfer uploaded image attachments to S3 and surface them within the platform and on Service Reports.

With this architecture, we have the potential to reduce storage needs for image files within Salesforce.

Outside of Field Service, there are many other use cases for reducing image storage needs. Another popular scenario, for example, is for Salesforce-powered web experiences in which users need to submit image files.


## Next Steps and Resources

Here are some ideas for taking this architecture further:



* Implementing removal of the original Salesforce files once S3 has them
* Creating a custom Lightning Web Component to display the uploaded images
* Extending this implementation to support other file types, such as PDF documents

Here are some useful links related to this implementation:



* [AWS Serverless Image Handler](https://aws.amazon.com/solutions/implementations/serverless-image-handler/)
* The [Sharp Library](https://sharp.pixelplumbing.com/), which provides the image processing capability we use in AWS