views:

350

answers:

6

I am in the process of moving all of the images in my web application over to a CDN but I want to easily be able to switch the CDN on or off without having to hard code the path to the images.

My first thought was to add an HttpHandler for image extensions that depending whether a variable in the web.config (something like ) will serve the image from the server or from the CDN. But after giving this a little though I think I've essentially ruled this out as it will cause ASP.NET to handle the request for every single image, thus adding overhead, and it might actually completely mitigate the benefits of using a CDN.

An alternative approach is, since all of my pages inherit from a base page class, I could create a function in the base class that determines what path to serve the files from based off the web.config variable. I would then do something like this in the markup:

<img src='<%= GetImagePath()/image.png' />

I think this is probably what I'll have to end up doing, but it seems a little clunky to me. I also envision problems with the old .NET error of not being able to modify the control collection because of the "<%=" though the "<%#" solution will probably work.

Any thoughts or ideas on how to implement this?

A: 

you could loop all controls and change the images url in the prerender event on your base class...

Petoj
+1  A: 

You've dismissed writing an HttpHandler based on an assumption of pre-optimization. I would revisit this and definitely write a simple HttpHandler and test it out. You might find that your Page method solution might even be slower, especially if you get the ASP preprocessor involved.

HttpHandlers are pretty close to the metal - it's a miniscule amount of overhead for IIS to hand the request to ASP.Net. It would be a more elegant solution than what you're proposing, and probably more scalable and I'm willing to bet - faster.

womp
Yeah, I'm not sure why I didn't even realize that second approach gets the ASP.net process involved . . . pretty thoughtless on my part. I guess my biggest concern is that each image request would request the image from the CDN and possibly mitigate the client-side caching done from the response headers of the CDN? I don't know enough about what's going on under the hood to make that determination.
Chris
A: 

The good thing about the HTTP Handler approach is that it's quite re-usable and configurable: you can identify img paths to handle based on location - assuming the structure they're in helps this.

The possible drawback is that image file extensions (.jpg, .png, etc) aren't automatically passed on to the asp.net pipe-line; you can easily config IIS to do so - but you need to have a certain level of contriol over IIS - so it might not be an option if you're on a shared hosting environment.

Adrian K
A: 

Hi Chris

Have you considered a slightly simpler approach?

If your pages all inherit from a base class, you could expose a property on that which contains the prepend URL to your CDN (or, to your local server if you want to switch the CDN off). It is then a trivial matter of storing the prepend URL in the web.config:

public string PrependURLPath() {
 get { return ConfigurationManager.AppSettings["ImagePrependURL"].ToString(); }
}

In your <appSettings/> element, you can simply choose what the prepend URL would be, eg:

http://my.cdn.com/user/

or:

http://my.own.server.com/images/

Pretty simple!

You would then be able to code your image refernces as per your example, but calling your base page property to expose the desired path:

<img src='<%= this.BasePage.PrependURLPath() + [YourImagePath.png] %>'/>

I agree that setting the image source through the inline call is messy, but you could probably do as someone else has suggested and then iterate through the image controls on your page, changing the prepend URL as you go.

Even if your pages currently only inherit from System.Web.UI.Page, it's a simple matter to create your own base class which inherits System.Web.Page, then do a find/replace in your solution on all remaining pages.

Hope this helps.

Richard
A: 

If you display your images using tags you could create a control adapter, these allow you to alter the way .net controls render or universally alter them something like this should do the trick:

using System.Web.UI.WebControls.Adapters;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace ExampleCode
{
    public class ImageAdapter : WebControlAdapter
    {
        private bool UseCdn
        {
            get { return true; } // Get value from config or anywhere else
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            Image image = (Image)Control;

            if (UseCdn)
            {
                // If using relative urls for images may need to handle ~
                image.ImageUrl = String.Format("{0}/{1}", "CDN URL", image.ImageUrl);
            }
         }
      }
 }

Then add a browser file to the App_Browsers folder in your web project like below:

<browsers>
    <browser refID="Default">
      <controlAdapters>
        <adapter
          controlType="System.Web.UI.WebControls.Image"
          adapterType="ExampleCode.ImageAdapter"
          />
      </controlAdapters>
    </browser>
</browsers>
Rhys Godfrey
A: 

great post, the Image control adapter helped a lot. 3 cheer for Rhys

Jignesh