Assign images to content on upload in Umbraco
Tags: umbraco, dotnet
Published: 2021-09-13 20:04:46

Let's say you are creating an Umbraco site that has fx a product catalogue that is imported. You may have a lot of different products that need images as well, more than you are interested in going through one by one.

Here is an example on how you can automate it, this example will use the Umbraco Starter kit as it already has a product catalogue with SKU's:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d91lhj2p551z11h8w1bn.png)

First thing first - let's limit the automation to a "Product Uploads" folder in the media library:

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

We will save the ID of this folder in a constants file:

```csharp
namespace AutoImages.Constants
{
    public static class Content
    {
        public const int MediaUploadFolder = 1145;
    }
}
``` 

Then we will create a media event to listen for uploads to that folder. For this we can use the cacherefresher event - it is the last event after media saving, plus this way we can trigger it for everything by rebuilding the cache:

```csharp
using Umbraco.Core.Composing;
using Umbraco.Core.Services;
using Umbraco.Web.Cache;

namespace AutoImages.Events
{
    public class MediaEvents : IComponent
    {
        public void Initialize()
        {
            MediaCacheRefresher.CacheUpdated += MediaCacheRefresher_CacheUpdated;
        }

        public void Terminate()
        {
            MediaCacheRefresher.CacheUpdated -= MediaCacheRefresher_CacheUpdated;
        }

        private void MediaCacheRefresher_CacheUpdated(MediaCacheRefresher sender, Umbraco.Core.Cache.CacheRefresherEventArgs args)
        {
            // do stuff
        }        
    }
}
```

Before diving into the event code we need to make sure it runs on the site, for that we need to register it in a composer:

```csharp
using AutoImages.Events;
using Umbraco.Core;
using Umbraco.Core.Composing;

namespace AutoImages.Composers
{
    public class SiteComposer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            composition.Components().Append<MediaEvents>();
        }
    }
}
```

Alright so now that the basics are laid out - what we actually want to happen is something like this:

1. Image is uploaded to our uploads folder
2. That image has some sort of naming convention we can use to match it with the product nodes
3. We search the product nodes to find a match
4. If one is found we "select" the image on that node

To make it easy we will just say we will overwrite the product image if the name matches the sku:

```csharp
using Examine;
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Changes;
using Umbraco.Core.Sync;
using Umbraco.Web.Cache;

namespace AutoImages.Events
{
    public class MediaEvents : IComponent
    {
        private readonly IContentService _contentService;
        private readonly IMediaService _mediaService;
        private readonly IExamineManager _examineManager;

        public MediaEvents(IContentService contentService, IMediaService mediaService, IExamineManager examineManager)
        {
            _contentService = contentService;
            _mediaService = mediaService;
            _examineManager = examineManager;
        }

        private void MediaCacheRefresher_CacheUpdated(MediaCacheRefresher sender, Umbraco.Core.Cache.CacheRefresherEventArgs args)
        {
            if (args.MessageType != MessageType.RefreshByPayload)
                throw new NotSupportedException();

            var products = new Dictionary<string, string>();

            // for each media item the cache has refreshed
            foreach (var payload in (MediaCacheRefresher.JsonPayload[])args.MessageObject)
            {
                // only when the node is refreshed - so not on delete, move, etc
                if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshNode))
                {
                    var media = _mediaService.GetById(payload.Id);
                    if (media == null || media.Trashed) continue;

                    // only continue if the parent is the uploads folder
                    if (media.ParentId == Constants.Content.MediaUploadFolder)
                    {
                        // example name: UMB-BIKER-JACKET.png
                        var splitName = media.Name.Split('.');

                        // we save the sku and the media udi for later
                        products.Add(splitName[0], media.GetUdi().ToString());
                    }
                }
            }

            _examineManager.TryGetIndex(Umbraco.Core.Constants.UmbracoIndexes.ExternalIndexName, out IIndex index);
            var searcher = index.GetSearcher();

            foreach (var product in products)
            {
                var searchResults = searcher.CreateQuery().NativeQuery($"__NodeTypeAlias: product +sku: {product.Key}").Execute();

                if (searchResults.Any())
                {
                    foreach (var searchResult in searchResults)
                    {
                        if (!int.TryParse(searchResult.Id, out int contentId))
                            continue;

                        var node = _contentService.GetById(contentId);

                        // MediaPicker v2 saves the udi of the image, which was saved in the value of the product dictionary already
                        node.SetValue("photos", product.Value);
                        _contentService.SaveAndPublish(node);
                    }
                }
            }
        }

        public void Initialize()
        {
            MediaCacheRefresher.CacheUpdated += MediaCacheRefresher_CacheUpdated;
        }

        public void Terminate()
        {
            MediaCacheRefresher.CacheUpdated -= MediaCacheRefresher_CacheUpdated;
        }             
    }
}
```

And here is a gif of it all working 😉

![autoImages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9dvc37rkr660sb2ibxh.gif)

That's it for now! I am not entirely happy with having to use services as much as I do, so if anyone know of a more performant way to do this please let me know!

It is however still way faster than having to do it all manually!