Try our conversational search powered by Generative AI!

Anders Hattestad
Feb 25, 2011
  5397
(12 votes)

Scale image using a virtual path provider and DDS

Itera.Media is a module that uses a virtual path provider to save and serve scaled images. But it uses a file share to store the scaled images.

But after I have been a DDS geek, I ported this function to use the DDS as the store.

The concept is simple I hook up on all requests that starts with ~/ImageScaler/

for instance

/ImageScaler/Global/StartPage/cms6_headerEN.jpg/width_202.jpg

the rest of the path is the file I want to resize (Global/StartPage/cms6_headerEN.jpg),

and the filename is the parameters I want to resize with (width_202.jpg)

I wanted a provider that was just to add a dll into the bin folder, so I used a Virtual File Provider auto registration method

Code Snippet
  1. public class StartUp : PlugInAttribute
  2. {
  3.     public static void Start()
  4.     {
  5.         System.Diagnostics.Debug.Write("VppInitializerX Loading Itera.ImageScaleProvider VPP from: " + typeof(StartUp).Assembly.CodeBase);
  6.         Assembly lateBoundAssembly = System.Reflection.Assembly.LoadFrom(typeof(StartUp).Assembly.CodeBase);
  7.         HostingEnvironment.RegisterVirtualPathProvider(
  8.             (VirtualPathProvider)lateBoundAssembly.CreateInstance(
  9.                 "Itera.ImageScaleProvider.ImageScaler_Provider"
  10.             )
  11.         );
  12.     }
  13. }

The actually method that returns the stream looks like this

Code Snippet
  1. public override System.IO.Stream Open()
  2. {
  3.     string orgFilePath = "";
  4.     string parameters = "";
  5.     SplitIntoOrgAndParameters(ref orgFilePath, ref parameters);
  6.  
  7.     var orgFil = System.Web.Hosting.HostingEnvironment.VirtualPathProvider.GetFile(orgFilePath) as UnifiedFile;
  8.     if (orgFil == null)
  9.         return null;
  10.     RedirectToLoginIfNotAllowed(orgFil);
  11.  
  12.     var dbFile = ScaledImages.GetCreate(virtualPath.ToLower());
  13.     Stream stream = new MemoryStream();
  14.     if (dbFile.Created == DateTime.MinValue || orgFil.Changed> dbFile.Created)
  15.     {
  16.         CreateImage(orgFilePath, parameters, orgFil, dbFile, stream);
  17.     }
  18.     else
  19.     {
  20.         stream.Write(dbFile.ImageData, 0, dbFile.ImageData.Length);
  21.         stream.Seek(0, SeekOrigin.Begin);
  22.     }
  23.     return stream;
  24. }

First I find the original file, and try to load it.If it exists I check the ACL to ensure that none can access a restricted file.

Then I check the Dynamic Data Store table to check if the images exists there, and is newer than the original image.

The images is stored in a DDS table that looks like this. The image is stored as a byte[] array

Code Snippet
  1. class ScaledImages : IDynamicData
  2. {
  3.     public EPiServer.Data.Identity Id { get; set; }
  4.     public string RequestUrl { get; set; }
  5.     public DateTime Created { get; set; }
  6.     public byte[] ImageData { get; set; }
  7.     public double TimeUsedCreating { get; set; }
  8.     public static  ScaledImages GetCreate(string requestUrl)
  9.     {
  10.         var query = from item in Items where item.RequestUrl == requestUrl select item;
  11.         var result = new ScaledImages() { RequestUrl = requestUrl };
  12.         foreach (var item in query)
  13.         {
  14.             result = item;
  15.         }
  16.         return result;
  17.     }
  18.     public static IOrderedQueryable<ScaledImages> Items
  19.     {
  20.         get
  21.         {
  22.             return Store.Items<ScaledImages>();
  23.         }
  24.     }
  25.     public static DynamicDataStore Store
  26.     {
  27.         get
  28.         {
  29.                 
  30.             return DynamicDataStoreFactory.Instance.GetStore(typeof(ScaledImages)) ?? DynamicDataStoreFactory.Instance.CreateStore(typeof(ScaledImages));
  31.         }
  32.     }
  33. }

So I can access the resized files using some of these filename

/ImageScaler/Global/StartPage/cms6_headerEN.jpg/

width_200.jpg

width_200.height_200.color_black.mode_FillArea.jpg

width_200.height_200.color_black.mode_FillAreaWithCrop.jpg

width_200.height_200.color_black.mode_MaxWidthOrHeight.jpg

width_200.height_200.color_black.mode_FillAreaWithCrop.pos_Left.jpg

The resize code, is some old code my brother made, but it works :)

The DDS looks like this

image

And are logging time used created, so its easy to find problems at a later stage.

There is only one drawback. The Virtual Path is added to the web site, and it works, but for some strange reason I get this error

image

If I add this to web.config

Code Snippet
  1. <location path="ImageScaler">
  2.   <system.webServer>
  3.     <handlers>
  4.       <clear />
  5.       <add name="wildcard" path="*" verb="*" type="EPiServer.Web.StaticFileHandler, EPiServer" />
  6.     </handlers>
  7.   </system.webServer>
  8. </location>

it works.

So I have to add stuff in web.config, until I can sort out why I get the error.

Full code is here

Feb 25, 2011

Comments

Anders Hattestad
Anders Hattestad Feb 25, 2011 12:02 PM

Seriously, is this blog post a 1 star rating? (was 1 vote with 1 when I wrote this).

The code here is in my option valid, and could benefit more developers. Could you please explain what made this a 1 rating?

Feb 25, 2011 12:19 PM

I'm not the one who voted, but this opens up for a very easy DOS attack.

Anders Hattestad
Anders Hattestad Feb 25, 2011 12:26 PM

Thats true, but that is easy fixable by only have some attributes values that are allowed.

or one could only be allowed to retriew rows that exists in the database. And have a method that creates an empty row when you outpur the img tag.

But good point, and is easy to forget.

seth@blendinteractive.com
seth@blendinteractive.com Feb 25, 2011 01:20 PM

Definitely not worth just one star, a great proof of concept and base. I know a lot of editors who really want the functionality of scaling images or doing more to edit them and have multiple sizes as part of the upload process.

Please login to comment.
Latest blogs
Optimizely and the never-ending story of the missing globe!

I've worked with Optimizely CMS for 14 years, and there are two things I'm obsessed with: Link validation and the globe that keeps disappearing on...

Tomas Hensrud Gulla | Apr 18, 2024 | Syndicated blog

Visitor Groups Usage Report For Optimizely CMS 12

This add-on offers detailed information on how visitor groups are used and how effective they are within Optimizely CMS. Editors can monitor and...

Adnan Zameer | Apr 18, 2024 | Syndicated blog

Azure AI Language – Abstractive Summarisation in Optimizely CMS

In this article, I show how the abstraction summarisation feature provided by the Azure AI Language platform, can be used within Optimizely CMS to...

Anil Patel | Apr 18, 2024 | Syndicated blog

Fix your Search & Navigation (Find) indexing job, please

Once upon a time, a colleague asked me to look into a customer database with weird spikes in database log usage. (You might start to wonder why I a...

Quan Mai | Apr 17, 2024 | Syndicated blog

The A/A Test: What You Need to Know

Sure, we all know what an A/B test can do. But what is an A/A test? How is it different? With an A/B test, we know that we can take a webpage (our...

Lindsey Rogers | Apr 15, 2024

.Net Core Timezone ID's Windows vs Linux

Hey all, First post here and I would like to talk about Timezone ID's and How Windows and Linux systems use different IDs. We currently run a .NET...

sheider | Apr 15, 2024