Using ColdFusion's CFLocation Tag For Inline Image SRC Attributes

Posted May 17, 2007 at 2:50 PM

Tags: ColdFusion

Not so long ago, I discovered that you could use ColdFusion's CFLocation tag in conjunction with inline image tag SRC attributes. I actually found this out by accident. Before hand, I would have never assumed that this would work. The idea here is that the SRC attribute points to a proxy page which then CFLocates the request on to the actual image file. How does this work? No idea... as far as I am concerned, it's just some crazy voodoo browser request magic.

To help explain what I am talking about, take a look at this HTML page:

 Launch code in new window » Download code as text file »

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>ColdFusion CFLocation And Image Soures</title>
  • <style type="text/css">
  • body {
  • font: 12px georgia ;
  • }
  • </style>
  • </head>
  • <body>
  •  
  • <p>
  • Hey dude, you gotta check out the photo of
  • this chick that I found:
  • </p>
  •  
  • <p>
  • <!--- NOTE: Image proxy. --->
  • <img
  • src="image_proxy.cfm?id=1"
  • width="316"
  • height="217"
  • border="0"
  • />
  • </p>
  •  
  • <p>
  • She's a bit thick, but I think it goes to
  • show that woman of all shapes and sizes can
  • be smokin' hot!
  • </p>
  •  
  • </body>
  • </html>

Notice that the SRC attribute of the IMG tag points to image_proxy.cfm ColdFusion template, not to an actual, physical image file. Now, if the image_proxy.cfm template used CFContent to return an image binary, that would make sense to me; but, as you will see, the image_proxy.cfm template simply forwards the request to the image file:

 Launch code in new window » Download code as text file »

  • <!--- Kill extra output. --->
  • <cfsilent>
  •  
  • <!--- Param the image ID that will be coming in. --->
  • <cfparam
  • name="URL.id"
  • type="string"
  • default=""
  • />
  •  
  •  
  • <!---
  • Check the ID to see if this is a valid photo ID
  • request. If it is, we are going to set the target
  • URL so that we only need ONE CFLocation tag.
  • --->
  • <cfswitch expression="#URL.id#">
  • <cfcase value="1">
  • <cfset strURL = "thick_chick_still_a_hottie.jpg" />
  • </cfcase>
  • <cfdefaultcase>
  • <cfset strURL = "image_not_found.gif" />
  • </cfdefaultcase>
  • </cfswitch>
  •  
  •  
  • <!---
  • ASSERT: At this point, even if we were given an
  • invalid image ID, we are going to have a target URL
  • thanks to the CFDefaultCase tag.
  • --->
  •  
  • <!--- Forward image request to actual image file. --->
  • <cflocation
  • url="#strURL#"
  • addtoken="false"
  • />
  •  
  • </cfsilent>

Again, notice that ColdFusion is not really dealing with the images directly; it's just forwarding the browser's request on to the image. Now, the ColdFusion CFLocation tag actually sends a header value back to the browser which means that the IMG tag loads a page, which returns a header, which then forwards the request to the image.

Not sure how that works, but it does:


 
 
 

 
CFLocation For Use With Inline IMG tag SRC Attributes  
 
 
 

Pretty cool, eh? This could be really cool if you don't have a fancy image database or anything, but you don't want the image location fixed. You could set up a proxy that then knows where the image directory is and can forward appropriately.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

Doug Bezona
May 17, 2007 at 3:15 PM // reply »
6 Comments

Not much mystery here - HTTP is a very simple protocol - a GET is a GET, whether it's triggered by typing a URL, or a IMG SRC attribute - the browser fetches it in exactly the same way, and as long as the final result to the browser is something it expects, it will work fine.

This is a really handy tip though - even though it makes perfect sense that it works, it hadn't ever occurred to me to try this...


May 17, 2007 at 3:20 PM // reply »
6,371 Comments

@Doug,

I understand that the HTTP request protocol is very simple. I guess what makes it hard for me (mental visualization) is that when you request a page, no problem, it the primary request... but then on the page it has to load various other things like SCRIPT tags and IMG tags... those are like sub-requests that can then interact with the returned headers.

I guess I can't think of it as a single page request - it's really multiple data requests that the browser then merges (ie. putting in image content into a different request data set). Just seems funky to think about. I get it, just seems funky.

Glad you find it a good tip though.


May 17, 2007 at 3:30 PM // reply »
23 Comments

One particular situation I use this mechanism for quite frequently is generating and caching dynamic content. To follow the image theme, if you want to generate thumbnails for your photo gallery, you point your thumbnails at tn.cfm?id=1234, which then gets the full-size image, shrinks it, writes it to cache storage, and redirects to the cache file. Subsequent requests can check for existence of the cache file, and if it's already there, just redirect directly.

I actually use mod_rewrite to do that (which saves the extra HTTP roundtrip), but the concept is the same. Managing the cache is a snap too, just delete the cache file and it'll be regenerated on next request. If you name you cache files intelligently, you can create very granular caching mechanisms for dynamic content, but (if you're using mod_rewrite) serve it in a 100% static way, without involving the app server in any way.


May 17, 2007 at 3:34 PM // reply »
6,371 Comments

@Barney,

Good suggestion. I like the idea of the thumbnail generation (but more so, the cache idea in general). Thanks. Caching is something that I don't do enough of (my poor server!).


Doug Bezona
May 17, 2007 at 7:49 PM // reply »
6 Comments

@Ben

Oh, I understand - it's not necessarily intuitive, but when you look as http log files it's pretty clear that, under the hood, all the requests are really the same, and that it's the browser that molds it all together into the proper context.

The only reason I am underscoring the point is this is something that a lot of people seem to not quite "get", and I've just seen a couple of examples of it in the last few days.


May 17, 2007 at 7:57 PM // reply »
7 Comments

Ben, there's another use: track visualizations in e-mails.

You can use a image inside the e-mail, via <img src=""> pointing to a ColdFusion page, passing URL parameters to identify which user openned and correctly visualized the e-mail.

BTW, your blog became obligatory nowadays!

Best wishes,

Fernando S. Trevisan


May 17, 2007 at 10:57 PM // reply »
15 Comments

Fernando, et. all

I recently wrote an email tracker as part of a CRM system using this trick not too long ago. Unfortunately, Outlook doesn't automatically download the image so tracking only occurs when the image is downloaded. Do you know of anyway to force the image to download, so tracking work regardless..??


May 18, 2007 at 1:51 AM // reply »
23 Comments

Many (most?) email clients don't automatically download images for exactly this reason. It's a rather devious mechanism from the perspective of non-techie users, and as such, should be frowned upon. Therefore, clients disable it by default.

If you need to include images in emails, much better to inline the images. Makes the email bigger, and removes the ability to [sneakily] track reads, but it's a trusted method for getting image content in front of users.


duncan
May 18, 2007 at 5:19 AM // reply »
18 Comments

Ben, what's the advantage of using the cflocation over the cfcontent? The code is slightly neater, but apart from that I don't see any difference in how the image is served.


May 18, 2007 at 5:47 AM // reply »
7 Comments

Alan,

As far as I know, there's no way to track which user received the e-mail, even when using "request read receipt", user has the right to answer yes or no. As I said in my comment, this is a way to track which user correctly visualized your e-mail; if you send it as a full image, the only way user can see the contents is downloading the image and then... :)

Barney,

I agree with you, but there's a lot of good reasons to use this type of verification. In a intranet, you can send reports to users and then scheduled warnings for those who doesn't correctly visualized it. And it's just a dummy example.


May 18, 2007 at 7:12 AM // reply »
6,371 Comments

@Fernando,

Firstly, thanks for the kind words. I am glad you appreciate the stuff that I do. Secondly, I think what you are referring to actually has a name. I think it's called an "email bug", but that may have been something someone just told me in passing.

I heard somewhere that while email clients do not automatically download images, more often, they will download external style sheet links. I assume you could do the same thing with the URL and tracking.

@Duncan,

The advantage of using CFLocation over CFContent is one of speed. If you use CFContent, then ColdFusion has to set aside a thread that takes care of reading in the binary and streaming it to the client. While it can do this, it is SLOOOOOW. If you use CFLocation instead, then IIS (in my case) takes care of streaming the file to the browser and it is much faster.

The downside is that CFLocation must use web-accessible URLs where as CFContent can use private directory access. But there are work arounds for that.


Simon Free
May 20, 2007 at 10:36 PM // reply »
31 Comments

@Fernando

I experimented with using a full image in an email, this wasn't to track anything, the clients design was just so crazy i didn't want to slice it. One problem i found though while testing was that it often went into my spam folder, especially in gmail, Outlook and Hotmail. Do you know how to get around that?

-Si


May 21, 2007 at 9:35 PM // reply »
7 Comments

I've used to insert text relative to the e-mail, but with the background color and 1px size. But it was a long time ago, I don't know if this still work.


May 21, 2007 at 11:46 PM // reply »
15 Comments

I've been pondering the same question too. Why does some email go into Junk folders while other does not.

I'd also be interested in anyone's thoughts on this. Is it a ratio of text to images? An unsubscribe line?, how the images are handled within an email? Simpicity of the email, or complexity? I've lightly experimented with this a little today. But if anyone has some knowldege on this, it'd be desirable to know from a best practice sort of thing.


May 22, 2007 at 2:10 AM // reply »
23 Comments

Alan,

There are a pile of rules that are used. Best bet is to get access to an email account that has visibility into it's spam filtering. SpamAssassin is one that I've used, and it'll send you a nice report about each message that it tagged as spam, with a breakdown of what factors influenced it's decision. High HTML-to-text ratio is one factor that I recall being a significant detriment to a given message's spam score.


Oliver
Oct 13, 2009 at 8:43 AM // reply »
2 Comments

Does anyone have an actual example of how to implement an email bug.

I have tried this but it doesn't seem to work in terms of updated the DB to let me know that the user has viewed the email I have sent. Using outlook 2007.

Firstly:
I include the img in the email being sent:
<img src="#Application.http_host#track.cfm?track_id="123">

Secondly::
In the track.cfm I have the following code

<cfset transGif = "#GetDirectoryFromPath(ExpandPath('*.*'))#images\blank.gif">
<cfparam name="url.track_id" default="0">

<CFQUERY name="qUpdate" datasource="#Application.dsn#" username="#Application.usr#" password="#Application.psw#">
UPDATE mytable
SET has_viewed = 1
WHERE uuid = <CFQUERYPARAM cfsqltype="CF_SQL_VARCHAR" value="#url.track_id#">
</cfquery>

<cfcontent type="image/gif" file="#transGif#">

Problem:
When I open the email I just sent to myself in outlook it doesn't update the db column has_viewed.

Any help would be greatly appreciated..

Thanks
O


Oct 13, 2009 at 10:20 AM // reply »
7 Comments

Oliver, if the code you posted is exactly the one you`re using, maybe the problem is with the line:

<img src="#Application.http_host#track.cfm?track_id="123">

You may want to use it this way:

<img src="#Application.http_host#track.cfm?track_id=123">

Please note that I removed the second " before the number, that must "close" the src info and what the CF page would get was:

[yoursite]track.cfm?track_id=

As you have a param setting the track_id to 0 the script would not throw an error and also would not update the field as it would be always 0.

Just a thought.


Oliver
Oct 15, 2009 at 1:45 AM // reply »
2 Comments

Fernando,

What a rookie mistake I have made.. Thanks soo much.

Removing the param setting did the trick.
I actually pasted the line incorrectly. I had the formatting correct...

Thanks again.
O


Oct 15, 2009 at 7:55 AM // reply »
6,371 Comments

@Oliver,

Also keep in mind that this will only work IF the email client allows the image to be loaded (from what I have read). A lot of clients prevent this from happening by default (only with approval from user).

That said, I have used 3rd party email campaign software (Campaign Monitor) that uses this technique and it appears that a LOT of people do actually load images in their email, so I guess this really does work.


Post Comment  |  Ask Ben

Recent Blog Comments
Nov 7, 2009 at 5:53 PM
Ask Ben: Javascript String Replace Method
You can find here an advanced function that prepared with javascript replace function. This can make the first letters of words, sentences, lines and whatever you define automatically: http://www.m ... read »
Andrew Neely
Nov 7, 2009 at 4:56 PM
A Moment That Touched Me - The Fountainhead
Ben, Glad you enjoyed the podcast. Yeah, the Tank Riot guys can get really chatty during the episodes, but that's part of the charm of it for me. They've covered everything from Nichola Tesla to Cha ... read »
Nov 7, 2009 at 4:43 PM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
Is it possible to make some more MenĂ¼`s ? ... read »
Jill
Nov 7, 2009 at 11:40 AM
How To Unformat Your Code (Like A Pro)
Derek, I think you might be right - sweet! Thanks for the link :) ... read »
Nov 7, 2009 at 11:25 AM
How To Unformat Your Code (Like A Pro)
I think it would be way easier to just use this http://www.logichammer.com/html-formatter/ He just released v3 and it rocks. ... read »
Jill
Nov 7, 2009 at 7:58 AM
How To Unformat Your Code (Like A Pro)
LMAO - this was pretty funny! I have to admit - I also love to reformat code so I can read it. My boss used to tell me to leave my OCD at home. Now I don't feel so bad after reading everyone else' ... read »
Nov 6, 2009 at 10:10 PM
How To Unformat Your Code (Like A Pro)
The timing of this post is just uncanny. I spent the last 15-20 minutes manually un-formatting my "Ben Nadel" style code within a CFC of mine. I was really digging the readability a few weeks ago, bu ... read »
Roe
Nov 6, 2009 at 5:11 PM
Passing Arrays By Reference In ColdFusion - SWEEET!
ArraySort also reorders the results of these java obj's ... read »