Ensuring image extension for content URLs
When using an image resizing component, like we do in the Focal Point plugin, it's really important that the file extension is there. If it's not, the resizing engine won't kick in for the request, and that enormous image is sent straight to your mobile users, and they will forever hate your guts.
I've implemented a solution using the new (in CMS 10) IUrlSegmentCreator events, and a regular InitializationModule. Code below. It uses the first registered extension for your media type as the default extension for the URL segment. This works for me, since I have separate Media types for all image files, like JpegImage, PngImage etc, to be able to more granularly control their usage, through the AllowedTypes attribute. If you have a single MediaData for all your images, I recommend altering the extension mapping to maybe use the MimeType property and get an appropriate extension based on that.
/*using System;
using System.IO;
using System.Linq;
using EPiServer;
using EPiServer.Core;
using EPiServer.Framework;
using EPiServer.Framework.DataAnnotations;
using EPiServer.Framework.Initialization;
using EPiServer.Web;
*/
[InitializableModule, ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class ImageDataInitialization : IInitializableModule {
private bool eventsAttached;
public void Initialize(InitializationEngine context) {
if(!this.eventsAttached) {
var creator = context.Locate.Advanced.GetInstance<IUrlSegmentCreator>();
creator.Created += CreatedMediaSegment;
this.eventsAttached = true;
}
}
private static void CreatedMediaSegment(object sender, UrlSegmentEventArgs e) {
var content = e.Content as ImageData;
if(content != null) {
var extension = Path.GetExtension(content.RouteSegment);
if(string.IsNullOrWhiteSpace(extension)) {
var type = content.GetOriginalType();
var fileExtension = GetExtensionForType(type);
if(!string.IsNullOrWhiteSpace(fileExtension)) {
content.RouteSegment = content.RouteSegment + "." + fileExtension;
}
}
}
}
private static string GetExtensionForType(Type contentType) {
var mediaDescriptorAttribute = contentType?.GetCustomAttributes(typeof(MediaDescriptorAttribute), false).OfType<MediaDescriptorAttribute>().FirstOrDefault();
return mediaDescriptorAttribute?.Extensions?.FirstOrDefault();
}
public void Uninitialize(InitializationEngine context) {
var creator = context.Locate.Advanced.GetInstance<IUrlSegmentCreator>();
creator.Created -= CreatedMediaSegment;
this.eventsAttached = false;
}
}
I'm not fan of hacks, but theoretically if you happen to have project with extension less images (which is weird) in it, you can tell IR to kick-in. See Nathanael answer.
http://stackoverflow.com/questions/14387595/imageresizer-and-image-files-without-an-extension
Thanks, but that seems like a can of worms. I cannot know all paths that csn, or cannot, contain an image. I think I'll stick with this approach. :)