Classic ASP: "Push" File Downloads from A Directory Outside the Application Root

Filed under: IIS, Quick Code, VBScript — Written by Chrissy on Monday, January 22nd, 2007 @ 4:04 am

This is some super old code but I used it recently and figured I'd archive it on this site for my future reference. The sample code below aims to allow authenticated users to download files which are not available via direct download (ie. files within the web root). The script accomplishes this by doing the following:

1. Checks to see if the user is logged in (your method may vary)
2. Sets the root directory location
3. Checks to see if the file exists, if so...
4. Retrieves the filesize and adds the appropriate HTTP headers including content disposition, filename, content type and filesize.
5. Uses a binary stream to "push" the download

Save this file as download.asp and call it with the filename in the querystring. Example: http://domain.com/downloads/download.asp?filename=myfile.pdf. Also, be sure to give read permissions to IUSR_SvrName to the root directory. Change the authentication requirements as needed:

download.asp

<%
If session("loggedIn") = True Then
 
  strFilePath = "D:\webfiles\downloads"  & request.querystring("filename")
 
  Set objFSO = Server.CreateObject("Scripting.FileSystemObject")
    If objFSO.FileExists(strFilePath) Then
      Set objFile = objFSO.GetFile(strFilePath)
      intFileSize = objFile.Size
      Set objFile = Nothing
 
      strFileName = request.querystring("filename")
      strFileName = replace(request.querystring("filename")," ","-")
      Response.AddHeader "Content-Disposition","attachment; filename=" & strFileName
 
      Response.ContentType = "application/x-msdownload"
      Response.AddHeader "Content-Length", intFileSize
 
      Set objStream = Server.CreateObject("ADODB.Stream")
        objStream.Open
        objStream.Type = 1 'adTypeBinary
        objStream.LoadFromFile strFilePath
        Do While Not objStream.EOS And Response.IsClientConnected
        Response.BinaryWrite objStream.Read(1024)
        Response.Flush()
        Loop
        objStream.Close
      Set objStream = Nothing
    Else
      Response.write "Error finding file."
    End if
  Set objFSO = Nothing
End If
%>

Note: Even if the file is a PDF and a third-party application such as Adobe Reader is set to open the file within the browser, the code below will override that and force a download (by using the "application/x-msdownload" content type).

[The UPDATE below is no longer accurate as an alternative solution has been given below in the comments and subsequently, was added to the code (Thanks a bunch, David!). I wanted to leave it for Googlers looking for a solution, however]

UPDATE: Someone wrote to let me know that they were encountering the error "Response Buffer Limit Exceeded". As it turns out, IIS 6's ASPBufferingLimit is set to a measly 4MB (4194304 bits) so any file over 4MB would produce this error. To fix this issue, you will have to have access to IIS either via the command line or the MMC. Here's how to change the buffering limit via the command line:

************ NOTE: An easier solution is to use the updated Do While/Flush procedure given in the code *****************

cd C:\inetpub\adminscripts
cscript adsutil.vbs set /w3svc/aspbufferinglimit 4294967295

That's a buffering limit of more than 4 Gigabytes. Personally, I'd lop off the last digit and make that number closer to 430MB. Running that script worked immediately on my test machine, even though I do not have "Enable Direct Metabase Edit" checked in IIS' Properties. If it doesn't work for you, restart IIS and see if it works.

7 Comments   -
  • Comment by Bonny | February 8, 2007 @ 9:59 am

    Hai Dude,

    That was a good example..Easy to understand.. Nice presentation.. This is the first time i do mail to a persons article..Keep up your good work.. All these stuff will help us to get projects done in correct time and will give a rescue from our senior people :) LOL !!!!!!

    Anyway thanks a lot..

    Bye

  • Comment by Lee | February 11, 2007 @ 11:59 pm

    Good article and code. Do you know if your going to use mp3 files how I can get it so it saves as a mp3? Right now with your code it just saves the file as the name of the script (file.asp). It would be nice to save as songname.mp3. Anythoughts?

  • Comment by Chrissy | February 12, 2007 @ 1:25 am

    Hey Lee,
    You are right..sorry about that, I did a mass replace and replaced one too many variables. The following line

    "Content-Disposition","attachment; strFileName=" & strFileName

    should read
    "Content-Disposition","attachment; filename=" & strFileName

    I will update the code.

    Chrissy

  • Comment by David | June 25, 2007 @ 5:33 am

    Thanks for the example. This really helped me move along with a project. I was able to get around the buffering limit simply by reading and writing in chunks which should probably be done anyway. Instead of the one BinaryWrite call, replace with the following:

    Do While Not objStream.EOS And Response.IsClientConnected
    Response.BinaryWrite objStream.Read(1024)
    Response.Flush()
    Loop

    I no longer receive the buffering limit message.

  • Comment by Chrissy | July 19, 2007 @ 4:43 am

    David,
    EXCELLENT Solution! Thanks so much!

  • Comment by Andeh | September 6, 2007 @ 11:22 pm

    Been looking for something like this for past 2 days.

    Threw it on my Dev site, changed it a bit, and works like a charm.

    Thanks :D

  • Comment by Inamorty | February 22, 2008 @ 9:27 am

    Generally, google is as useful as a one-legged man in an arse-kicking competition for finding info like this quickly but today i've hit the jackpot. Thanks a million dude!

Leave your comment