Secure File Downloads Using ColdFusion 7

Share on FacebookTweet about this on TwitterShare on LinkedInGoogle+

Share on FacebookTweet about this on TwitterShare on LinkedInGoogle+

It is occasionally necessary to control who can download a file. The typical method of doing this in a web application is to store the file somewhere outside of the document root and use an application server to process the request and output the file. Under ColdFusion, this is normally done with a combination of the tag to reset page output and programmatically send the file, and the tag to set HTTP headers like Cache-Control and Content-Disposition. You will usually use something like this to send an Excel spreadsheet and ask the browser to open it: 

For the most part, this solution works pretty well. However, there are two issues that crop up with this approach: Memory usage, and threads.

Memory Overload

Prior to CF8, using <cfcontent> to send a file, actually attemptted to load the entire file into memory before sending it out. So if you had a 10MB file and 100 people were attempting to download it, you could be using ~1GB of RAM for all these downloads. If you had a 2GB or larger file, your download would probably not work because the JVM that underlies CF is only able to address a maximum of 2GB of RAM.

While CF8 64-bit removes this 2GB memory limitation on 64-bit systems, it also uses buffered I/O to send the file, only keeping a small portion of it in memory at time.

Thread Starved

If you have larger files you want to serve up along with a decent number of users you will likely run into a lack of threads. While the user is downloading the file through , CF is actually pumping out the bits, keeping the Java thread in use.

This can quickly lead to thread starvation, meaning other users on the system start experiencing extremely sluggish speeds and even errors, waiting for someone else’s download to finish. This problem is present regardless of the CF version you’re on.


Re-Evaluate Your Security Requirements

In some instances absolute security is not required. When that’s the case, you can work around these problems by using a solution such as Ben Nadel’s Semi-Secure File Downloads.


If you’re an Apache user, there’s a solution that may well fix your problem – mod_xsendfile. This Apache module allows the application server — be it PHP, CF, perl, etc. — to set an HTTP header that causes the web server to send the file on behalf of the application server.

This uses a minimal amount of memory, and it releases the application server thread allowing Apache to do what it does best — pump out the bits.

If you’re not using Apache (I’m really surprised there isn’t an IIS filter for this) or can’t use mod_xsendfile for whatever reason, things get more complicated.

Custom Code

You can tune the thread count, but that can lead to other performance problems. You can grow your cluster (increasing the total number of available threads) or hand the downloads off to a dedicated download server — but these solutions all require additional hardware costs. Without a mod_xsendfile solution, or a bigger budget, you’re pretty much stuck.

You could try implementing a download queue — thereby only allowing only a few downloads at any one time — but this is fraught with issues, like how to determine if a user has cancelled a download, or if a download is stalled. It’s very easy to quickly run out of download slots, making it impossible for anyone to download.

We can however, help out with the memory situation. While we can’t fix the 2GB limitation, we can implement a buffered I/O solution for CF7 using Java, keeping downloads to ~80k of memory use, regardless of the size of the files being downloaded or how long it takes to download them.

Let’s get started with some code.

Buffered File Sender

We know we’ll need the file name and mime type, just as <cfcontent> does. But how will we take control of the server output? Fortunately CF gives us access to the underlying java servlet PageContext (javax.servlet.jsp.PageContext) through the getPageContext() method, and the HTTPServeletResponse (javax.servlet.http.HttpServletResponse) via getPageContext().getResponse().getContext().

The instances of these classes will allow us to reset the output buffer, set the mime type, set other headers like Content-Length, and Content-Disposition, as well as flush the data in the buffer out over the network. Looping over a BufferedInputStream ( will allow us to read small chunks of data from the file, and write to the context’s output (

package com.seguetech.coldfusion.util;
// These imports require the jrun.jar to be on your classpath.
import javax.servlet.jsp.PageContext;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletResponse;
* When a file download is passed via the CFCONTENT tag in CF7 and earlier,
* CF reads the entire file into memory before sending it out.
* This class can at least help with resolving the memory issue by using
* buffered i/o to read the file in, and explicitly flushing the output buffer.
public class BufferedFileSender {
  // the default mime-type, if one is not specified.
  protected static final String mimeType = “application/octet-stream”;
  // since this is a secure file download, we want to disable caching.
  protected static final boolean disableCaching = true;
  // by default, send as an “attachment”, or download file, rather
  // than attempting to load the file in the browser window.
  protected static final boolean sendInline = false;
  public BufferedFileSender() {}
  public static void send(String fileName, PageContext context) throws Exception {
  public static void send(
   String fileName, PageContext context, String mimeType
  ) throws Exception {
  public static void send(
   String fileName, PageContext context, String mimeType, boolean sendInline
  ) throws Exception {
  public static void send(
   String fileName, PageContext context, String mimeType, boolean sendInline,
   boolean disableCaching
  ) throws Exception {
   // input file
   File inFile = new File(fileName);
   // clear the output buffer, if we’ve tried to send anything.
   // use reflection to get the HttpServletResponse.
   // This method isn’t part of the standard object, it’s CF specific.
   HttpServletResponse response = (HttpServletResponse)context.getResponse().getClass().getMethod(“getResponse”, new Class[] {}).invoke(context.getResponse(), new Object[] {});
   // double set the content type, just in case.
   * since setContentLength() only take an int, and file.length()
   * returns a long, we use setHeader instead and pass it manually
   * as a string, since that’s what setContentLength() would output
   * anyway.
   response.setHeader(”Content-Length”, Long.toString(inFile.length()));
   // If caching is disabled, then output the headers that disable caching.
   if (disableCaching) {
     “Sat, 6 May 1995 12:00:00 GMT”
   “no-store, no-cache, must-revalidate”
  // bend IE to our will.
   “post-check=0, pre-check=0″
  // initialize the disposition.
  String disposition = null;
  // if inline is enabled, send it inline.
  if (sendInline) {
   disposition = “inline; filename=” + inFile.getName();
  // otherwise send it as an attachment (download).
  else {
   disposition = “attachement; filename=” + inFile.getName();
  // set the content-disposition, which will cause the file to be downloaded,
  // rather than open in the browser.
  // get a buffered input stream, read up to 64kb into memory at a time.
  BufferedInputStream bis = new BufferedInputStream(
   new FileInputStream(inFile),
  // get the output stream
  OutputStream os = response.getOutputStream();
  // this is our binary data.
  int data;
  // this is a counter.
  int count = 0;
  // loop while we have data.
  while ((data = != -1) {
   // increment the count.
   count = count++;
   // write the data to the output stream.
   // perform an explicit flush to the client every 16k
   if (count == 16384) {
    count = 0;
   // close the input stream.
  * This method call releases the page context. This prevents any further
  * output to the browser, but unlike it does not prevent code from
  * running after this, which has some potentially interesting uses

Compile this class (requires jrun.jar) and drop it in your [JRUN_HOME]serverscfusioncfusion-earcfusion-warWEB-INFlib folder (or the equivalent for your platform and installation), and restart your ColdFusion server to activate it. Once installed, you can run it like this:

 (”c:myfile.txt”,getPageContext(),true) />

 (”c:myfile.txt”,getPageContext(),”text/html”,false,true) />

 (”c:myfile.xls”,getPageContext(),”application/”,true) />


Share on FacebookTweet about this on TwitterShare on LinkedInGoogle+

About the Author

Mark R. Andrachek, Jr. has been a developer for over 20 years (5 years with Segue Technologies, Inc.) with experience building everything from small web pages to architecting large scale enterprise applications for customers in Education, Banking, Non-Profit, and Government markets, utilizing a broad range of platforms and technologies. His Java and Linux experience made Android just the right fit for his entry into mobile, where he's led the way for Segue since the creation of our first Android application in 2010. In addition to his mobile work, Mark has continued to develop and support Segue's efforts on the USAF MPES project. He lives in Crewe, Virginia with his wife, 3 children, three cats, and a dog. Read more from Mark Andrachek