luxor37
luxor37

Reputation: 48

How to get image width with Umbraco Media Service Saving Event

I am looking for a way to get the width of an image with an Umbraco event, to validate that the image respects a certain aspect ratio before being uploaded. The thing is I don't know if casting the mediaItem to Image is even possible. How can I get the width or the height here is my event code so far:

public class ImageResizer : ApplicationEventHandler
{
    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        MediaService.Saving += MediaServiceSaving;
    }
    void MediaServiceSaving(IMediaService sender, SaveEventArgs<IMedia> e)
    {
        foreach (var mediaItem in e.SavedEntities)
        {
            Image img = (Image)mediaItem;
            double height = img.Height;
            double width = img.Width;
            string msg = "" + height;
            if (height / width != 1)
            {
                msg = "Ratio is not 1:1. Please make sure the width and height of your image is the same.";
                BasePage.Current.ClientTools.ShowSpeechBubble(BasePage.speechBubbleIcon.success, "Error", msg);
            }
            else
            {
                //Ducplicate image to images and thubnails of desired choice
                //Send images to database - continue normal process
            }
        }
    }
}

Upvotes: 1

Views: 1945

Answers (2)

Mario Lopez
Mario Lopez

Reputation: 1465

Just trust Umbraco and let it do the hard work of figuring out if it's an image or not:

public class ImageResizer : ApplicationEventHandler
{
        protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {
            MediaService.Saving += MediaServiceSaving;
        }
        void MediaServiceSaving(IMediaService sender, SaveEventArgs<IMedia> e)
        {
            foreach (var mediaItem in e.SavedEntities)
            {
                //if it's an image, the content type will tell you.
                if(mediaItem.ContentType.Alias == "Image")
                {
                    var width = Convert.ToDouble(mediaItem.Properties["umbracoWidth"].Value);
                    var height = Convert.ToDouble(mediaItem.Properties["umbracoHeight"].Value);

                    if (height / width != 1)
                    {                        
                  //Sending a message will cancel the process, so you don't
                  //need an else (unless you want to do something else with the image of course.
                        e.Messages.Add(new EventMessage("Wrong Ratio", "Ratio is not 1:1. Please make sure the width and height of your image is the same.", EventMessageType.Error));                        
                    }                    
                }
            }
        }
}

Upvotes: 1

Claus
Claus

Reputation: 1985

You can't really do it like this. First of all, IMedia is not necessarily an image - it could be anything (video, pdf, zip and so on). It is just an item being saved in the media library of Umbraco. You can actually go as far as saying that an Umbraco media item doesn't even have to have a file attached to it (and it could potentially also have multiple files if you wanted).

Besides - even if the media item was actually an image, you can't simply cast it to the .NET Image class as they are not really the same thing.

You want to verify that an uploaded image follows certain proportions before allowing it to be saved. While you can see the proportions of the uploaded image in Umbraco, this is not really something that is always available at the moment where your code is running. When a media item is being saved, the event you are hooked into, triggers.

Along with your event handler, a different event handler is also attached to this exact event. This other event handler is responsible for adding the umbracoWidth and umbracoHeight properties based on the dimensions of the uploaded image. So you can't really rely on these properties to always be assigned and available at the moment where your code is running. You don't know the exact order these events are running in, or whether the dimensions are actually persisted at the moment where your code is being hit.

To do what you want to do, you would need to access the file data of what is being saved, and from that - generate the Image in memory and use that to check the dimensions of the image being uploaded.

All in all, I however don't really think your attempt here is the best way to solve this problem. If someone is uploading multiple images where only half of them are allowed - how would you handle this scenario? It would not be very clear for the user which files were correct and which weren't when simply just showing notification bubbles. Apart from that - you're not really preventing anything in your code example above. You're simply showing a notification error - but the file would still be saved even though the proportions are incorrect.

Then there's the issue of hooking into a general event like the Saving event on the media service. This is used for any type of media being saved - so first of all you would need to make sure your code only runs on things you want it to run on. This means limiting it to be run only when actually uploading an image. You however also most likely want to limit it to only run on uploads happening in specific cases (it seems a bit unlikely that you only ever want uploads to fit a 1:1 ratio no matter what).

I think the best solution to your problem would be to look into creating a custom image upload editor for the media library that simply checks the image directly in javascript, whether it fits your ratio requirements. This would ensure you can handle cases of people trying to input incorrectly sized images, in a nice way - showing a warning and invalidating the state of the UI, before even letting them attempt to save the media item.

It will also be more flexible in the way that it doesn't interfere with all media saves being made in your site - the code would only handle uploads where this specific property editor is used for the upload.


I would also highly recommend that you spend a little time looking into the features of ImageProcessor and the Image Cropper in Umbraco. It basically allows you to set specific pre-set sizes you want to have your images in. When an image is uploaded, you can specify precisely how you want this image to be cropped/resized for a specific size. These crops can then be used in your markup and Umbraco will make sure to resize, crop, cache and output the image in your site in the specific size you request in your templates.

You can read more about it here: https://our.umbraco.org/documentation/getting-started/backoffice/property-editors/built-in-property-editors/image-cropper

Upvotes: 1

Related Questions