Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with:

Tracking File-Download Events Using JavaScript And ColdFusion

Posted by Ben Nadel

In my ColdFusion applications, I either use the CFContent tag or the X-SendFile module to stream binary data back to the client as a download. Then, in the browser, I simply link to the ColdFusion page that delivers the binary response. This works perfectly well; but, it's not great for client-side events because "Content-Disposition: attachment" doesn't trigger a "load" event on the client. As such, it's very difficult to know, in the JavaScript, when the download event has executed successfully. Luckily, this morning, I stumbled across a Stack Overflow answer that had a great suggestion - track the download event using cookies!


 
 
 

 
 
 
 
 

The concept here is super simple:

  1. Have the server-side download page set a Cookie.
  2. Check for that cookie on the client-side.

Since the cookie headers will only arrive on the client when the download response has arrived, it means that the mere existence of the cookie will indicate that the user has completed the download request. Brilliant in its simplicity!

To test this, I set up a page with a download link. Then, using jQuery, I intercept the navigation event (on the link) and append the cookie value that the server will echo. Working with cookies on the client is, in my experience, extremely frustrating; as such, my demo does only the bare minimum needed to perform the test in this context.

Index.cfm - Our Client-Side Code

  • <!doctype>
  • <html>
  • <head>
  • <title>
  • Tracking File-Download Events Using JavaScript And ColdFusion
  • </title>
  • </head>
  • <body>
  •  
  • <h1>
  • Tracking File-Download Events Using JavaScript And ColdFusion
  • </h1>
  •  
  • <p>
  • <a href="./download.cfm" class="download">Download the file</a>
  • </p>
  •  
  • <!---
  • Hide the Thank You until the user actually executes the
  • target download.
  • --->
  • <p class="thanks" style="display: none ;">
  • <strong style="background-color: yellow ;">
  • Thank you for your download!
  • </strong>
  • </p>
  •  
  •  
  • <script
  • type="text/javascript"
  • src="//codeorigin.jquery.com/jquery-2.0.3.min.js">
  • </script>
  • <script type="text/javascript">
  •  
  • $( "a.download" ).click(
  • function( event ) {
  •  
  • var target = event.target;
  •  
  • // When tracking the download, we're going to have
  • // the server echo back a cookie that will be set
  • // when the download Response has been received.
  • var downloadID = ( new Date() ).getTime();
  •  
  • // Update the URL that is *currently being requested*
  • // to contain the downloadID. This will then be response
  • // cookie header.
  • target.href += ( "?downloadID=" + downloadID );
  •  
  • // The local cookie cache is defined in the browser
  • // as one large string; we need to search for the
  • // name-value pattern with the above ID.
  • var cookiePattern = new RegExp( ( "downloadID=" + downloadID ), "i" );
  •  
  • // Now, we need to start watching the local Cookies to
  • // see when the download ID has been updated by the
  • // response headers.
  • var cookieTimer = setInterval( checkCookies, 500 );
  •  
  •  
  • // I check the local cookies for an update.
  • function checkCookies() {
  •  
  • // If the local cookies have been updated, clear
  • // the timer and say thanks!
  • if ( document.cookie.search( cookiePattern ) >= 0 ) {
  •  
  • clearInterval( cookieTimer );
  •  
  • $( "p.thanks" ).show();
  •  
  • return(
  • console.log( "Download complete!!" )
  • );
  •  
  • }
  •  
  • console.log(
  • "File still downloading...",
  • new Date().getTime()
  • );
  •  
  • }
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

As you can see, the JavaScript appends a "downloadID" value to the outgoing request URL. Then, it starts a timer, waiting for that downloadID to show up in the local, client-side cookies. This downloadID cookies gets set using the CFCookie tag on the server:

Download.cfm - Our Server-Side Code

  • <!---
  • This is the download-tracker value that we will need to set
  • in the response so that the client-side knows when the download
  • has been executed.
  • --->
  • <cfparam name="url.downloadID" type="string" default="" />
  •  
  •  
  • <!--- Pause here to simulate download preperation. --->
  • <cfset sleep( 5 * 1000 ) />
  •  
  •  
  • <!--- Set the tracking cookie. --->
  • <cfcookie
  • name="downloadID"
  • value="#url.downloadID#"
  • />
  •  
  • <!--- Define the download content. --->
  • <cfheader
  • name="content-disposition"
  • value="attachment; filename=download.txt"
  • />
  •  
  • <cfcontent
  • type="application/octetstream; charset=utf-8"
  • variable="#charsetDecode( 'Download file content.', 'utf-8' )#"
  • />

That's all there is to it! If I make a request to the download page, which has a sleep() command for several seconds, I get the following console output:

File still downloading... 1379695746067
File still downloading... 1379695746565
File still downloading... 1379695747066
File still downloading... 1379695747565
File still downloading... 1379695748066
File still downloading... 1379695748568
File still downloading... 1379695749066
File still downloading... 1379695749585
File still downloading... 1379695750065
File still downloading... 1379695750567
Download complete!!

How sweet is that! This is so simple, I can't believe I've never seen this before! I'll definitely be incorporating this approach in my ColdFusion applications.



Reader Comments

I like. It makes it easy to respond gracefully to the download activity and success.

<requested crack comment upload complete>

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.