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

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.

Chrissy is a PowerShell MVP who has worked in IT for nearly 20 years, and currently serves as a Sr. Database Engineer in Belgium. Always an avid scripter, she attended the Monad session at Microsoft’s Professional Developers Conference in Los Angeles back in 2005 and has worked and played with PowerShell ever since. Chrissy is currently pursuing an MS in Systems Engineering at Regis University and helps maintain RealCajunRecipes.com in her spare time. She holds a number of certifications, including those relating to SQL Server, SuSE Linux, SharePoint and network security. She recently became co-lead of the SQL PASS PowerShell Virtual Chapter. You can follow her on Twitter at @cl.

Posted in IIS, VBScript
35 comments on “Classic ASP: "Push" File Downloads from A Directory Outside the Application Root
  1. Bonny says:

    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

  2. Lee says:

    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?

  3. Chrissy says:

    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

  4. xps says:

    i hate the webcam. i didn't realise you could buy one without :/ i havn't even installed the crappy logitech drivers, so it's a redundent eye-sore.

  5. David says:

    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.

  6. Chrissy says:

    David,
    EXCELLENT Solution! Thanks so much!

  7. Andeh says:

    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

  8. Inamorty says:

    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!

  9. Ronnell says:

    I agree with everybody else, I could not find this anywhere on google and this was the answer to all my questions. Thanks a million!

  10. Monty says:

    Fighting with this for past one month and getting refused by the hosting company to changes its IIS 6’s ASPBufferingLimit for me, i thought my application’s life is over, until today. Thanks a Trillion!

  11. InfoQuick says:

    Is there any way for me to trigger the download dialog box from a remote file store? My .mp3 file is hosted at Amazon S3 and is accessed using a URL.

    I have the .mp3 files local but don’t want to deal with the bandwidth issues when the site becomes popular.

    Or, how about using HTML or Javascript instead of ASP?

  12. Tim says:

    Excellent! Thank you so much for posting this!

  13. Sandelo says:

    This is awesome! Worked like a charm with the user authentication I already had setup. Thanks for posting and making updates!

  14. Sean says:

    WOW! This little piece of code just made my day. I had been looking for something to do this for a while.

    Thanks m8

  15. Al says:

    Is there a way to show a progress bar for really large files? It seems to work but a 200-300mb file will churn for quite some time without screen feedback

  16. Kola Grey says:

    Thank you very much Chrissy. This is exactly what I was looking for.

  17. Brian says:

    Wow thank you so much, I have been pulling my hair out trying to find a way to DL larger files and still hide the dir, thanks so much!!

  18. Bent says:

    Thx
    very much for this code,

  19. Dave says:

    Like many of the preceding comments, this snippet of code was exactly what the doctor ordered.

    Like yourself, had an ancient site that required a solution just like yours.

    Cheers – David

  20. alok says:

    how to download file at root direcory without asking save as option in asp.net

  21. mrithu says:

    I have used your code and when i download i am getting an erro like this in chrome.I am new to this asp classic.

    If i upload a file of samller size i am able to download but if it is little big also i am facing this problem, what should i do now.

    The webpage at http://www.indiaroot.com/news/download.asp?File=%… might be temporarily down or it may have moved permanently to a new web address.
    Error 346 (net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH): Unknown error.

  22. PROG says:

    THANKS A LOT FOR THIS CODE!!!

  23. Sam says:

    Doing something like

    filePath = “D:\WebFiles\Downloads\” & Request.QueryString(“name”)

    and then using it directly is awful, it will allow anybody to download any file on the whole web server, as a user can pass something like ..\database_connection_strings.asp and download the source code of the page (or even more sensitive files).

  24. Grateful user says:

    Thanks for this excellent code.

    Surely there is a missing in this line, between path and filename ?

    strFilePath = "D:webfilesdownloads" & request.querystring("filename")

    Also, is there a way to offer 'save as' rather than forcing display on-screen ?

  25. wmoze says:

    The only other thing I added to this code was a check to make sure the user can't download the download.asp file as it contains a hardcoded ref to your server paths.

    if InStr(lcase(request.querystring("filename")), "download.asp") > 0 then
    response.write("can't download this file…")
    else
    '… code to load file
    end if

    You may also want to think about what kind of security issues this could cause as the user could potentially download any file on the server from the path listed in the code …

  26. Joao Falcao says:

    Hello,
    This script is fantastic but i need some help.
    if i download a file with safari it adds .exe to the end of the file name on download but firefox and i.e. does the work wright.
    Any ideas ??
    thank you
    Joao Falcao

  27. Bernd says:

    great piece of code. I revised it and put it in a self-contained function that has the full filename passed to it as an arg. Will post it once I have it running "live"

    • Madhu says:

      Were you able to make it live? I would like to see the code you written for the self-contained function.

      Thanks – Madhu

  28. Matt says:

    Thanks! I needed this so I could push files to users without using the ugly autogenerated filenames we use for storage.

  29. Daniele says:

    VERY WELL DONE!
    Thank you very much.
    I found that many people spends money for such very simple solutions.
    It works perfectly!
    Bye.
    Daniele

  30. Francesco says:

    Hi, just excellent work. Just a question:
    When I try to read the filename with FSO, if the filename is very long, the string is trimmed by ellipsis like that:
    “this is the real file name of my application.pdf”
    “this is the real … application.pdf”

    How I can solve this? Thank you in advance

  31. Chunks says:

    Very useful snippet, unfortunately for me it doesn’t seem to work.
    Well, it downloads a file, but it won’t open in anything, i’ve tried a pdf and a .png file and neither seem to open.

    “The file uygwfugye.png could not be opened. it may be damaged or use a file format that Preview doesn’t recognise.”

    Any idea whats going wrong?

  32. Moksaphoto says:

    Chunks, I think you must use a Windows 2012 server, that is why it does it. I used this code on my old Windows 2003 without any problem, but when I migrated my systems over, it stopped working and gave me the same result as you, it starts the download but finally I got a broken file. I did not change anything on the code, so it must be something with the OS and/or security settings or asomething else.

    Anybody has any idea?

  33. Harold Rau says:

    This script is great! It solved a problem I was having with a previous download script because of the II6 4 meg limit. Now my download script works great again.
    Thanks Chrissy for posting it.

  34. Guvenc says:

    Nice Script
    Working %100
    It is nice to see some asp codes around. :)

Leave a Reply

Your email address will not be published. Required fields are marked *

*