ASP.NET Performance Sin - Serving Images Dynamically (Or Another Reason To Love Fiddler)

Serving images dynamically may cause performance hit. Dynamically served images require more HTTP requests which violates Steve Souders' performance rule #1 - Make Fewer HTTP Requests. The latency is also caused by parallelism (or parallel downloading) limitations as described in detail here Performance Research, Part 4: Maximizing Parallel Downloads in the Carpool Lane

Static Images

Below are the series of images that served dynamically and static.

image

Static images displayed using GridView's ImageFiled column type. ImageField generates the following HTML mark-up:

<img src="IMAGES/Birds/icon-penguin.gif" style="border-width:0px;" />

Browser interprets it as a static image and is ready to cache it for further reuse.

Serving Images Dynamically

Below is the sample code that implements dynamic image serving. I witness in the field different variation but the pattern (I'd call it anti-pattern) remains the same. ASP.NET and HTML mark-up that is usually part of repeater control looks similar to the following:

<img src="ServeImage.ashx?FN =

<%#DataBinder.GetPropertyValue(Container.DataItem, "Image")%>" />

 

 

ASHX file's code that actually serves the image looks similar to this:

public void ProcessRequest(HttpContext context) { string imageFileName =

context.Request.MapPath(@"IMAGES\" + context.Request.QueryString["FN"]); context.Response.ContentType = "image/jpeg"; context.Response.WriteFile(imageFileName); context.Response.Flush(); context.Response.Close(); }

Network Analysis

Using one of my most favorite tools - Fiddler - it is easy to reveal browser's view on the traffic:

image

There is expiration attribute attached to static images while dynamically served images do not have such attribute.

Subsequent call the the same page that gets the same images reveals the following:

image

All dynamically served images are not cached and utilize the network on each request.

Further investigation shows, using Fiddler's P and C fantastic feature, that overall network utilization caused by these dynamically served images is about 350 KB, which could be saved by caching the images.

Recommendations

Avoid serving images dynamically. Follow best practices outlined at Exceptional Performance:

My relative posts

Published 02 May 08 02:12 by alikl

Comments

# DotNetKicks.com said on May 2, 2008 9:20 AM:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# Christopher_G_Lewis said on May 2, 2008 1:27 PM:

I figured out how to fix this in PhotoRoom

Between your ContentType and WriteFile add:

//Caching - Set the caching headers for this image.

context.Response.AddFileDependency(imageFileName );

context.Response.Cache.SetETagFromFileDependencies();

context.Response.Cache.SetLastModifiedFromFileDependencies();

context.Response.Cache.SetCacheability(HttpCacheability.Public);

context.Response.Cache.SetExpires(DateTime.Now.AddTicks(600));  //FromConfig

context.Response.Cache.SetMaxAge(999);  //FromConfig

context.Response.Cache.SetSlidingExpiration(true);

context.Response.Cache.SetValidUntilExpires(true);

context.Response.Cache.VaryByParams["*"] = true;

Chris

# alikl said on May 2, 2008 3:30 PM:

Chris!

That is pure gold nugget.

Thanks for sharing.

alikl

# foo said on June 1, 2008 8:22 PM:

Correct me if I am wrong but isn't this also a security issue if someone was to put "../../secretImageFolder/foo.jpg" as the request.querystring['fn'] param?

# alikl said on June 1, 2008 11:34 PM:

you are absolutely right.

# Relationship Compatibility said on June 7, 2008 8:00 AM:

Serving images dynamically may cause performance hit. Dynamically served images require more HTTP requests which violates Steve Souders' performance rule #1 - Make Fewer HTTP Requests . The latency is also caused by parallelism (or parallel downloading

# Alik Levin's said on June 11, 2009 4:56 AM:

&#160; &#160;&#160;&#160; This post is a quick overview of free performance tools available from Microsoft,

New Comments to this post are disabled

Search

Go

This Blog

. My Personal Blog .

.Net Performance How To's

.Net Security How To's

Design Patterns

Impactful

Lifecycle Phases

Popular

Tools

Syndication

Page view tracker