Creating A "Download Will Start Shortly" Page With ColdFusion

Posted June 5, 2007 at 8:36 AM by Ben Nadel

Tags: ColdFusion

Have you ever gone to a site that provides file downloads, but when you click the "Download Now" link, it servers up an intermediary page that says your file will be begin downloading shortly; and then, as if magically, seconds later, it prompts you for the file download. Most of the time, if you look at the source code to that page, you will find that there are no Javascript timeouts or Meta tag refreshes. So, how does page automagically server up a delayed file?

The magic lies in the page headers that get sent as the first pieces of content to the client browser. Page headers, while invisible to the end user, contain a ton of useful information and processing suggestions that change the way the browser will act. In this case, the page header we need to look at is the "refresh" command. The refresh header tells the client to refresh to a new location (URL) after a given delay (in seconds).

To demonstrate this, let's take a look at the pages involved. First, we need the page that provides a link to the file download:

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>ColdFusion Download Demo</title>
  • </head>
  • <body>
  •  
  • <h1>
  • Download Pictures
  • </h1>
  •  
  • <p>
  • <a href="./download.cfm">Click Here To Download</a>
  • </p>
  •  
  • </body>
  • </html>

This file does nothing else except take the user to the following page where the delayed download takes place. In our example, the file being downloaded is hard coded into the logic, but this is something could easily be passed in the URL as a file ID or some encrypted value:

  • <!--- Kill extra output. --->
  • <cfsilent>
  •  
  • <!---
  • Param the URL variable that will flag whether
  • or not we are actually performing the download or
  • whether we are showing the donwload landing page.
  •  
  • Since we are requiring this URL value to be of a
  • certain type, we need to wrap it in CFTry / CFCatch
  • tags when CFParaming it (as a bad param will throw
  • a data conversion exception).
  • --->
  • <cftry>
  •  
  • <cfparam
  • name="URL.download"
  • type="boolean"
  • default="false"
  • />
  •  
  • <!---
  • Catch any data conversion issues (if the
  • URL value is not a boolean, then an exception
  • will be thrown.
  • --->
  • <cfcatch>
  •  
  • <!--- Set to default false. --->
  • <cfset URL.download = false />
  •  
  • </cfcatch>
  • </cftry>
  •  
  •  
  • <!---
  • Check to see if we are downloading the target file.
  • If we are not, then we are going to shortly refresh.
  • --->
  • <cfif URL.download>
  •  
  • <!---
  • The file has been requested so now we have to
  • present it. You can do this through either a
  • CFLocation if the file is publically available
  • or through CFContet if you need to stream it
  • from a non-web-accessible file.
  • --->
  • <cflocation
  • url="./red_hot.jpg"
  • addtoken="false"
  • />
  •  
  •  
  • <!--- ... OR ... --->
  •  
  • <!---
  • The following will NOT be executed because of the
  • above CFLocation; it is here to demonstrate an
  • alternate way of getting at the file. CFHeader /
  • CFContent gives you more control of how the browser
  • will handle the target file, but it is not as
  • server-friendly as the CFLocation tag (uses more
  • processing power since ColdFusion is handling the
  • file stream.
  • --->
  • <cfheader
  • name="content-disposition"
  • value="attachment; filename=red_hot.jpg"
  • />
  •  
  • <!--- Stream the file to the client. --->
  • <cfcontent
  • type="image/jpeg"
  • file="#ExpandPath( './red_hot.jpg' )#"
  • />
  •  
  • <cfelse>
  •  
  • <!---
  • We have not started the download just yet. We are
  • going to be displaying the landing page and then
  • momentarily take the user to the download action.
  • In order to do so, we are going to send back a
  • refresh command in the page's headers.
  •  
  • 2: The number of seconds we are going to delay.
  • url: The url are the page is going to refresh to.
  • --->
  • <cfheader
  • name="refresh"
  • value="2; url=./download.cfm?download=1"
  • />
  •  
  • </cfif>
  •  
  • </cfsilent>
  •  
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>ColdFusion Download Demo</title>
  • </head>
  • <body>
  •  
  • <h1>
  • Download Pictures
  • </h1>
  •  
  • <p>
  • Thank you for your interest in downloading
  • this file. Your download should begin shortly.
  • </p>
  •  
  •  
  • <!---
  • In case something is wrong with the header
  • information, provide the user with a manual
  • link to accomplish exactly the same thing
  • that the refresh was going to be doing.
  • --->
  •  
  • <p>
  • If the download does not being in a few seconds,
  • click <a href="./download.cfm?download=1">here</a>
  • </p>
  •  
  • </body>
  • </html>

This page will be displayed for 2 seconds before the browser gets taken to the target file (red_hot.jpg). The trick here is the two modes the page is running in. Notice that we are defining the URL value, download. If this is false (which it is by default) then we display the intermediary download page. If this value if true then we either take the user to the target file or prompt them for download.

If the download value is false, meaning we are going to display the intermediary page, we set the refresh command using the ColdFusion CFHeader tag. This will set a header value to be sent to the client browser, in this case a 2 second pause followed by a forwarding to the url, ./download.cfm?download=1. This will take use back to the same page but with a different URL.download value. All header values must be set before any content is flushed to the client (as headers are and can only be the first pieces of data sent to the client).

In this demo, I am using CFLocation to forward the user to the target file. This is the easiest solution and puts much less strain on the ColdFusion server since the actual file transfer is handled by IIS. It does, however, require that the target files be web-accessible since CFLocation can only go to public URLs. CFHeader / CFContent, on the other hand, give you much more control over how the file is handled (inline vs. attachment, suggested file name) and where the files can be located (in a private folder), but they require that ColdFusion handle the file transfer, which can be a drain since it ties of processing power.



Reader Comments

Jun 5, 2007 at 11:45 AM // reply »
28 Comments

Very cool Ben. Thats a great tip.


Jun 5, 2007 at 2:04 PM // reply »
11,238 Comments

@Rey,

Thanks. It was something that I didn't think about very much, so I figured I would touch on the topic.


Jun 6, 2007 at 9:14 AM // reply »
95 Comments

Neat but why? He he. I was always annoyed at sites that did that. Does it really serve a purpose? Just give me the damn file...I don't want to wait. Just a small rant I guess.


Jun 6, 2007 at 9:27 AM // reply »
11,238 Comments

@Boyan,

No idea why pages do that... my only guess is that it gives them another chance to server up a page with advertising on it :)


Jun 9, 2007 at 7:32 PM // reply »
1 Comments

Ben, could the same strategy be used for a "waiting to process" page? For example, www.orbitz.com when you search for flights. Sometimes, it takes up to 1-2 minutes before the system returns...but while you are waiting you are getting a nice "Please wait while we find your flights..." message.

Or is that another whole ball of wax?


Jun 9, 2007 at 8:12 PM // reply »
11,238 Comments

@Ryan,

I think the same process can be applied there, most definitely. If you know that the processing page can take a long time, send the user first to an intermediary page that loads instantly (or just about) and then you can use the same Header / Location technique to take the user to the processing page. The only real difference, as I can see it, would be that you would have to send more variables via the URL; where we had just a download action flag, you would need to send through all the search criteria.


Sep 14, 2007 at 11:54 AM // reply »
1 Comments

We've adapted this CFM solution to PHP. We use the intermediate page to notify the users that the security settings of the file's target Microsoft application needs to be altered for the files to operate correctly.


Sep 14, 2007 at 11:57 AM // reply »
11,238 Comments

@G Calabrese,

That is a cool use for it. I didn't even think of anything like that.


Mar 19, 2008 at 5:21 PM // reply »
4 Comments

Can i redirect to another page after the download prompt? I have it set up with the cfcontent/header verbage, but the <cflocation> tag i have after that doesn't seem to execute.


Jun 4, 2010 at 2:13 AM // reply »
1 Comments

This code when run on IE 7 and higher does NOT download. It gives the yellow bar on top that says "internet explorer blocked this site from downloading fies". Is there a way to bypass this?


Jun 7, 2010 at 9:42 PM // reply »
11,238 Comments

@Dustin,

I don't think so, not with CFLocation. You can try to perhaps run a re-direct via Javascript with a setTimeout() on the rendered page.

@Swetha,

Hmm, I have not seen that before (I use mostly Firefox and Chrome). I am not sure what the best away around that would be. I'll see if I can find anything.


Apr 29, 2011 at 4:47 PM // reply »
1 Comments

@Ben

Thanks for your code.

I was trying to run this code, and browser ask me to download pdf file which is good, But it was not loading remaining HTML code or you can say I could not see "Thank you Message".

Am I doing something wrong or missing something?

Need your help.

  • <cfheader name="content-disposition" value="attachment; filename=134.pdf"/>
  •  
  • <cfcontent type="application/pdf" file="#ExpandPath( './134.pdf' )#"/>
  •  
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>Download Now</title>
  • </head>
  • <body>
  • <p>
  • Thank you for your interest in downloading
  • this file. Your download should begin shortly.
  • </p>
  • </body>
  • </html>


Jan 12, 2012 at 3:52 PM // reply »
3 Comments

Ben,

It's funny how these old topics come back again and again to visit, isn't it?

I have a slightly different problem. I am trying to download a vCard -- but by using the web browser on a mobile phone (Android or any, really), not on a PC.

I've tried using Ray Camden's cook book version (http://www.coldfusioncookbook.com/download, page 61), I've made sure the server had the MIME type of text/x-card set to vcf, I've tried various other web site solutions (such as http://lab.artlung.com/coldfusion-force-download/), and many different content-type's -- all to no avail.

Lest you think it can't be done, hit this link from your mobile phone or your PC: www.jackfruitdesign.com/old/downloadvcard/ (it worked fine on my Android). I'm guessing they are using PHP and not ColdFusion.

Thanks for any help you can provide!
RLS

P.S. Those looking for the vCard spec can find it at http://tools.ietf.org/html/rfc6350


Jan 12, 2012 at 4:59 PM // reply »
3 Comments

I actually am getting pretty close to resolving this issue, but when I try to import the file into my Android's Google contacts area, it says that instead of finding 'BEGIN:VCARD' it is finding '<!DOCTYPE HTML PUBLIC....', so something is shoving this in there somewhere.

How do I keep the "DOCTYPE" declaration out of the output? I've tried cfprocessing suppress whitespace, and cfsilent. CFFLUSH caused an error.

Here's the core code that's working so far:

<cfdirectory directory="d:\www.mysite.tld\www\" filter="forrestGump.vcf" name="fileInfo" type="file">
<CFSET fileSizeVCard=fileInfo.size>
<cfheader name="Content-Length" value="#fileSizeVCard#">
<cfheader name="Content-Type" value="application/octet-stream">
<cfheader name="Content-Disposition" value="attachment;filename=http://www.mysite.tld/forrestGump.vcf">
<cfheader name="Content-Location" value="#URLEncodedFormat('http://www.mysite.tld/forrestGump.vcf')#">
<cfcontent type="application/octet-stream" file="http://www.mysite.tld/forrestGump.vcf">


Mar 20, 2012 at 4:28 PM // reply »
11 Comments

Ben...once again, I need help figuring something out and your article comes up first in my search results. And you make it so simple!

Thanks for the help! Always appreciated.


May 5, 2013 at 11:21 PM // reply »
1 Comments

Don't. Don't be a halfwit. I want my download to start right NOW. Not in X seconds as you decree. What a monumental WASTE of human time.



Post A Comment

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.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 21, 2013 at 6:12 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
Ben, I did not see you after Pete Freitag's Lockdown session at cfObjective but he said that IIS sets file size limits at 30MB by default which just happened to be the threshold for file size when ... read »
May 21, 2013 at 11:51 AM
Ask Ben: Parsing Very Large XML Documents In ColdFusion
Looking at my first ever XML document that I have to parse and put into MS SQL 2000 with CF8. I get it to list the desired Field name, many times over, and have a long list of this field name displa ... read »
May 21, 2013 at 9:25 AM
Turning Off and On Identity Column in SQL Server
you are awesome..i am lucky to get this blog between such a garbage one....Thanks, Prashant ... read »
May 20, 2013 at 4:38 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, Your confusion is well founded, since this is a very confusing features. In fact, it ONLY works if you use array notation. Meaning, that this: arrayToList( query[ "columnName" ] ) ... read »
May 20, 2013 at 4:34 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I was thinking chicken and the egg, I wouldn't have expected it to work in the valuelist going in I guess. Maybe I just need a beer, long day :) ... read »
May 20, 2013 at 4:29 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, That's if you're trying to reference a specific row. In this case, we're trying to reference the entire query column as one cohesive value. So, you are correct that if you wanted to output a ... read »
May 20, 2013 at 4:24 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I thought when you used array notation to reference queries you always had to have the row or it would throw a similar error as well? ... read »
May 20, 2013 at 11:45 AM
Using jQuery's Animate() Step Callback Function To Create Custom Animations
This is really useful. I found out that you don't actually have to use a dummy css property (surprisingly). To animate a property in a linear-gradient for instance I did this this.css('someLinearGra ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools