Howto Force the Save As Dialog in the Browser

Your Web browser is set up to handle all kinds of documents. For example, when you open an .html page from a Web site, the browser knows it should parse this file, and display its rendered contents onscreen. The same is true for images; when you click on a link that directly links to an image, the image will be displayed in the browser.

This default behavior may not always be what you want. In some scenario's, it's useful to have the user download and save the file, instead of displaying it in the browser. This is a common requirement for files like images, Word documents, spread sheets and so on. This article will demonstrate you how you can force the client browser to present the Save As dialog, so your users can download the file and save it on their local hard disk.

Update - 02/11/2004

On 02/05/2004, Johnson Davis e-mailed me a work around for the problems with Netscape browsers like Netscape 7.0. If you move the download file to a sub folder, called /Downloads for example, and then make the download file the default file for the folder (in IIS this is usually done by renaming the file to default.asp, but you could also change the IIS settings so it uses your filename as the default file), the browser will look at the proposed attachment name instead of the name of the download file. So, in short, this is what you need to do:

  1. Create a folder called /Downloads
  2. Move the download file for this article (DownloadFile.asp) to the new Downloads folder and rename it as Default.asp
  3. Request the file in the browser by using http://YourServer/Downloads/ (note the absence of the file name, Default.asp)
Netscape will now offer the correct filename in the Enter name of file to save to... dialog. This work around also works fine in other browsers, like Internet Explorer and Opera.

Thanks for the tip, Johnson!

Prerequisites

The sample code presented in this article uses an ADODB.Stream object which is available in ADO since version 2.5. All sample code is in ASP, so obviously you need a server running ASP.

The Complete Code

You can take a look at the complete code by following this link. It will open in a new browser window, so it's easy to follow along with the explanation.

Forcing the download

To force the browser to display the Save As dialog, you'll need to fool it a bit. When you open a file in your browser by clicking a link or by typing in an URL in the Address bar, the browser examines the content type of the file you are requesting. Based on this content type, the browser determines what action to take. It searches a list of "registered applications" to see if one of them is set up to handle the requested content type.

If, however, you pass the browser a content type it doesn't know about, it automatically presents you with the Save As dialog. Changing the content type is easy in ASP; simply set the ContentType property of the Response object to application/x-unknown:

Response.ContentType = "application/x-unknown"
Once you have set up the content type, you can pass the name for the download to the browser as well. This name will be presented as the default name for the download. The user can change this name, however. The following code block declares two variables; One for the full name of the file you want to send to the browser. In this case, the code will send a Microsoft Word document to the browser. The other variable will hold the name as it appears in the Save As dialog in the browser:

Dim FileName
Dim FullFileName
FileName = "MyWordDocument.doc"
FullFileName = "C:\Webfiles\MyWordDocument.doc"
With the file names done, it's time to set the name for the file download. You'll need to use the AddHeader method of the Response object to do this. The chr(34) is used to send a double quote to the browser.
The Binarywrite method will write out a a variant array of unsigned one-byte characters. To get this Variant array, you can use an ADODB.Stream object. For easier access to the Stream object, I created a function called GetBinaryFile that instantiates and initializes this object. As a parameter it expects the full location of the file on disk, for example: C:\Webfiles\MyWordDocument.doc. For the sake of simplicity, the function itself is included in the ASP page, but you can place this function in an include file, so other pages can access it as well.

Response.Addheader "Content-Disposition", _
    "attachment; filename=" & chr(34) & FileName & chr(34)
Response.Binarywrite GetBinaryFile(FullFileName)
The GetBinaryFile method creates an instance of the ADODB.Stream object by calling Server.CreateObject and passing it the progID of ADODB.Stream. The stream is opened and its type set to adTypeBinary (to indicate you're sending a binary file, not plain text). The LoadFromFile method will load the file in FileSpec from disk, and then the Read method will read its entire contents. Because this code is executed in a function, you shouldn't send the output of the Stream to the Response object directly. Instead, it is returned it from the function, by setting the functionname equal to the result of the Read() method (GetBinaryFile = objStream.Read()).

Function GetBinaryFile(ByVal FileSpec)
  Const adTypeBinary = 1
  Dim objStream
  Set objStream = Server.CreateObject("ADODB.Stream")
  objStream.Open
  objStream.Type = adTypeBinary
  objStream.LoadFromFile (FileSpec)
  GetBinaryFile = objStream.Read()
  Set objStream = Nothing
End Function
The calling code will then use the BinaryWrite method to stream the file to the browser:

Response.Binarywrite GetBinaryFile(FullFileName)

Providing Access to the File

This example uses a hard coded filename for the file you want to stream to the browser. In a real site, this is not a very useful solution.
Instead, you can make this page dynamic so you can pass it the ID of a file you want to download through the QueryString. You can then transform this ID into a filename through a database lookup, a textfile, a Select Case statement, or any other solution you can come up with. The following code demonstrates the general principle:

Dim FileID
FileID = Request.QueryString("ID")
If Len(FileID) > 0 Then
  ' Pass the ID to a database and retrieve the filename
  ' FullFileName = Get the FileName from the Database
Else
  'Invalid ID
End If

' Rest of the download code with the GetBinaryFile method goes here.
If you save the file with the download code as DownloadFile.asp, you can the call this page like this:

<a href="Download.asp?ID=33">Download this File Now</a>
<a href="Download.asp?ID=34">Or Download this other File Now</a>
As you can see, the ID of the file is passed to DownloadFile.asp. Inside that file, the ID is used for a database lookup that retrieves the full filename

Added Benefits of Using a Stream Object

By sending the file to the browser using the Stream object, it's easier to secure your files. Usually, when you want to provide a file your user can download, the file must be within the scope of your Web site, for example www.yoursite.com/Downloads/YourFile.doc. This means people who know the URL of the file, can download it directly. If you want to protect the file, you can ask for a username and password, before you redirect your users to this URL. If, however, people already know the URL or can guess its name, they can circumvent your protection mechanism. All they need to do is type in the URL of the file directly and they can get access to it.

With the solution presented here, it's easier to block access to your file. All you need to do is check the user credentials (or any other business rules you may be using) at the top of the DownloadFile.asp. If you don't want your user to continue, simply redirect them away from the download file, as shown in the following pseudo code:

' Check User Rights Here
If UserOK Then
  ' Use the ID from the QueryString to lookup the FileName
  ' Stream the File to the Browser
Else
  Response.Redirect("NoRights.asp")
  Response.End
End If
This way, you users won't be able to access the file if they are not allowed to download it. Instead, they will be redirected to the page: NoRights.asp.

Disadvantages of the Stream Method.

Not all browsers will handle the proposed filename correctly. When you download the file in Netscape 6.2 for example, this is what you'll see:



Netscape thinks its downloading an ASP file, instead of a Word Document. As soon as you click Save this file to Disk and then click OK, Netscape will use the proposed filename (MyWordDocument.doc) for the download.

Each browser deals with this differently; Mozilla and Netscape 7, for example, use the proposed filename, but append an .asp extension to it, as shown if the following figure (from Mozilla 1.4):



In fact, the problem with these browsers is even worse as they also use this .asp extension in the proposed filename in the Enter name of file to save to dialog box. Your users will need to manually rename the file by removing the additional extension to make it a valid file. More recent versions of Mozilla (version 1.6) handle the downloaded file correctly, and propose the MyWordDocument.doc as the filename.

Summary

The method to download files presented in this article can be useful in a number of circumstances. First of all, it will allow your users to download a file, instead of having it opened within their browser.
Second, it allows you to put a tighter security mechanism in place; by validating the user before you stream the file, you can easily find out whether the user has access to the file.

The only drawback is that not all browsers handle the filename of the download correctly. If wide browser support is important to you, this solution is not very suitable. In other scenario's, like a company Intranet where you control the browser, this is an excellent way to control the files users can download from your site.

References

Download files


Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .


Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.



Feedback by Other Visitors of Imar.Spaanjaars.Com

On Wednesday, October 05, 2005 6:41:06 AM Ian said:
Dear Mr. Spaanjaars,

i have made a similar code to force download a file into client's PC. However i got a problem and i hope you can help me.

When downloading a file, IE show a download confirmation which ask the user where to put the file, is it a way to detect if the user press 'Cancel' button?.
I want to change the flag of the downloaded data, however once the 'force download dialog box' appears, i can't detect whether the user ACTUALLY download the file instead of pressing 'Cancel' button.

Thanks for ur attention.
Regards,

Ian
On Friday, October 07, 2005 7:57:09 PM Imar Spaanjaars said:
Hi Ian,

Sorry for the late response. Things are a bit crazy around here.

Anyway, AFAIK, this can't be done with a regular ASP page. There might be third-party controls available that do this, but I have never used them. One solution to this problem I often see is to allow the client to download the file multiple times during a specific time frame. After, say, a week or a day or so, the file can be blocked from being downloaded again.
That still doesn't tell you wether the file has actually been downloaded, but it might give your users at least a way to download it again if anything fails.

Sorry I don't have a better answer.

Cheers,

Imar
On Tuesday, December 20, 2005 8:02:44 AM James said:
My Imar,

My asp file needs to open / to save dialog box.  I implemented two lines of code as    
Response.AddHeader "Content-Disposition", "attachment; filename=CR" & Trim(strreference) & ".html"
Response.ContentType = "application/X-Unknown"
It works fine in some browser, unless the IE 6.0 with SP1 fail.  Suppose, I click on the Open; it will open a popup window to display a page.  The IE 6.0 with SP1 couldn't new a popup, and it displayed a page on the current window.

Please help.

Many thanks,
James
On Tuesday, December 20, 2005 8:34:13 AM Imar Spaanjaars said:
Hi James,

There is no need for a pop up to make this work.

In your main page, add something like this:

<a href="Download.asp?ID=33">Download this File Now</a>

Notice there is no target attribute, and no pop up code. Then Download.asp has the code from this article to stream the file.

That should be more than enough, no pop ups and no warnings while the file is still downloaded in a separate download window, leaving the original page as is was....

Hope this helps,

Imar
On Tuesday, December 20, 2005 8:54:54 AM James said:
Hi Imar,

My problem is needed the "File download" dialog window, and that "File download" dialog window has "Open", "Save", "Cancel" button.  The "Save" and "Cancel" buttons work fine, but the "Open" button could not open a new window in IE 6.0 SP1 display data, it not likes other browsers behavior.  The other version IE or Firefox will popup to open a new window to display data, but the IE 6.0 SP1 couldn't popup a new window.

Thanks,
James
On Thursday, December 29, 2005 6:20:20 PM Imar Spaanjaars said:
In that case I don't know what to say. I have never seen the behavior you described and I have always been able to successfully view / open a document downloaded with the code from this article even on XP with SP2 applied and IE with SP1 / SP2.

Are you using any special software or security settings other than SP2 OR SP1 for IE? That's the only thing I can think of....

Imar
On Thursday, August 17, 2006 3:59:39 PM Risho Mikus said:
Hello Imar,

I like te simplicity of the code. It works fine when I user in a test file, but as soon as I incorporate it with my application it doesn't.

Here the process: One file is responsible for uploading a file into a folder on the server. File get uploaded fine. On the download segment, file no. 1 is passing a file id to file no. 2 where I fetch for file name and file url from the database and force popup as you suggest in your code. When I select save or open I get an empty file in both cases.

Here is what the code looks like:

------------------------------------------------------------
SQL = "SELECT * FROM REQ_AttachedFiles WHERE FileID=" & Request.QueryString("ID")
Set RS=Conn.Execute(SQL)
'Response.Write("file: " & cfgRP & Trim(RS("FileURL")) & Trim(RS("FileName")))

If ( RS("FileURL") = "" ) Then
Response.ContentType = "application/octet-stream"
Response.AddHeader "Content-Disposition","attachment;filename=" & Trim(RS("FileName"))
Response.BinaryWrite RS("Attachment")
Else
Dim FileName
Dim FullFileName
FileName = Trim(RS("FileName"))
FullFileName = cfgRP & Trim(RS("FileURL")) & Trim(RS("FileName"))
Response.ContentType = "application/x-unknown"
Response.Addheader "Content-Disposition", "attachment; filename=" & chr(34) & FileName & chr(34)
Response.Binarywrite GetBinaryFile(FullFileName)

Function GetBinaryFile(ByVal FileSpec)
Const adTypeBinary = 1
Dim objStream
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Open()
objStream.Type = adTypeBinary
objStream.LoadFromFile (FileSpec)
GetBinaryFile = objStream.Read()
Set objStream = Nothing
End Function
End If
------------------------------------------------------------
Any explanation? Thanks.
On Saturday, August 19, 2006 11:23:05 PM Imar Spaanjaars said:
Hi Risho,

I am not sure I understand what you're describing here. What do you mean with "file no. 1 is passing a file id to file no. 2"?

I think you're better off posting this at a forum like http://p2p.wrox,.com and provide a bot more information and examples.

Cheers,

Imar
On Tuesday, August 22, 2006 10:04:54 AM Ian said:
Dear Mr. Imar,

Thank you very much for the information, i really appreciate it.
i'll try the code on the first time.

Regards,
Ian
On Wednesday, September 13, 2006 5:01:28 PM Rathi said:
Hi,

  When i open a file (in particular word / ppt/ PDF) in the browser i do not want to display the open/ save / cancel pop-up. How can i do that?

Please help me out in this.

Thanks a lot.

Rathi
On Saturday, September 16, 2006 11:37:03 AM Imar Spaanjaars said:
Hi Rathi,

Simply link to the file, like this: a href="SomeFile.pdf"

If the browser understands the file, it will display it; otherwise the user will still see a Save As dialog.

Cheers,

Imar
On Thursday, December 28, 2006 7:03:48 AM khalid Ahmed said:
i have a problem in dowloading files from I.E 7.0 when i try to download files i get a pop up window when i say save it starts the process and shows the result as 0 files copied ........Why is it so????

  if there is some specific code in asp 3.0 to Download files From Browser I.E 7.0 then please send me.


Thanks
On Thursday, December 28, 2006 9:53:15 AM Imar Spaanjaars said:
Hi khalid,

Are you sure the code is correct and you have set the right security permissions? I just tried the code from this article in IE7 and it worked fine...

Imar
On Thursday, April 05, 2007 5:12:17 PM Jay said:
Thanks for the article, it is very good.

The only problem I am experiencing is concerning the update regarding the Mozilla fix.

You say to reference the download script by just stating the default folder, such as:

/downloads/

When I try and reference this from a script I get an error message. But if I link to the actual name (/downloads/downloadfile.asp) it works okay.

Any thoughts?
On Thursday, April 05, 2007 7:19:41 PM Imar Spaanjaars said:
Hi Jay,

Make sure you've set up a default document for Default.asp on your web server.

If that doesn't help, I don't know what's causing the problem, especially since you didn't add what error message you got. You may want to post this on a forum like http://p2p.wrox.com and add some more details.

Cheers,

Imar
On Thursday, April 12, 2007 9:50:22 AM MikeB said:
Great code, thanks!
Is there a way I can redirect to a 'please wait' page while the file is downloading?
On Friday, July 06, 2007 11:13:05 AM DucTran said:
Hi Imar Spaanjaars,

The first thing, thanks for your shared code.
With your code is ok for me, but now i need to set my local path in the save as dialog. How can i set the local path when download any files?? is it possible??

Regards
On Friday, July 06, 2007 11:20:38 AM Imar Spaanjaars said:
Hi DucTran,

You can't. This is a browser setting and AFAIK this defaults to the last folder the user saved a file to.

In a low security enviroment you could control it by setting a registry key right before the file is saved. However, this involves changes to the computer's security settings and is thus only applicabe in a tighly controlled Intranet scenario using Internet Explorer

Cheers,

Imar
On Monday, June 23, 2008 10:11:20 AM Gitolekha Ray said:
The download process takes place in a pop-up window, so I want to close the pop-up automatically after the download completes and the save dialog box appears. But I cannot do it not even through javascript. it seems whatever I write after Response.BinaryTrasget(binData), nothing happens. Please help me!!
On Monday, June 23, 2008 11:19:54 AM Imar Spaanjaars said:
Hi Gitolekha,

The page that writes the file only writes the file. It cannot be followed by anything like HTML or JavaScript.

My advice: don't use a pop-up as the Save As dialog will already appear as a separate dialog.

Imar
On Saturday, February 21, 2009 12:44:08 PM Seth said:
Hi Imar,

Thanks for the help. I've been looking for an ASP solution to this problem for a while. A quick question.... I don't have that much experience with databases, but I would like to have the filename generated dynamically. How hard is it to get the ASP page to parse a text file for filenames and then match those against the ID passed by the original request?

Thanks!

Seth
On Saturday, February 21, 2009 12:51:02 PM Imar Spaanjaars said:
Hi Seth,

That shouldn't be too hard. In ASP you can use the FileSystemObject to read and parse a text file quite easily.

Cheers,

Imar
On Saturday, February 21, 2009 2:44:41 PM Seth said:
One other question... You mention that the fullfilename has to be the exact path to the file. On my local machine, running IIS, it's easy to see what that path is. But what about on a webserver?

I really appreciate the help.
On Saturday, February 21, 2009 3:32:02 PM Imar Spaanjaars said:
Hi Seth,

You can use Server.MapPath to "translate" a virtual path to a physical one. So, let's assume you have a web site stored in D:\Projects\MySite. Inside that folder there's a sub folder called Images containing a file called Whatever.jpg.

From a web perspective, the image can be accessed using /Images/Whatever.jpg. To get the physical location of that file, you can use:

location = Server.MapPath("/Images/Whatever.jpg")

The location variable will then contain D:\Projects\MySite\Images\Whatever.jpg

If the server uses a different physical root, it will return a different path.

Hope this helps,

Imar
On Saturday, February 21, 2009 3:54:32 PM Seth said:
Sweet! Working perfectly both local and remote. I appreciate the instant help!

Seth
On Saturday, February 21, 2009 3:59:43 PM Imar Spaanjaars said:
You're welcome....

Imar
On Thursday, May 28, 2009 6:52:20 AM tsgd84 said:
Once the save as dialog is shown, I saved the file. Everything gone fine, but later if i click any of the button in the parent which invoked the save as dialog, an error is thrown like

A runtime has occurred.
Do you wish to Debug?

Line: 115
Error: UnSpecified Error

Any idea which would have caused the issue?
On Thursday, May 28, 2009 4:35:07 PM Imar Spaanjaars said:
Hi tsgd84,

Don't know; never saw that error. What happens when you answer yes to the Debug question? Maybe that sheds some light?

Imar
On Friday, May 29, 2009 4:38:36 AM tsgd84 said:
When I click "Yes", It popup's another error

A runtime has occurred.
Do you wish to Debug?

Line: 211
Error: Object doesn't support this property or method

When I answer Yes for the above error, the page is as it is. Intended work is not done.
On Monday, December 21, 2009 5:42:50 AM redb said:
HI,

Can i make my save as dialog box to only save as pdf files... as in only pdf files and the FILE TYPE: ALL is not listed in the file type only .pdf!

thanks,
redb
On Monday, December 21, 2009 7:33:12 AM Imar Spaanjaars said:
Hi redb,

Nope, AFAIK, this can't be done.

Imar
On Monday, December 21, 2009 9:29:34 AM Seth said:
Hi Imar,

What a coincidence I got two emails this morning saying that there were replies here. I've been struggling to get your code to work with files over 4 MB, and I forgot where I got the code. The code works fine with files under 4 MB, but if I try anything larger, I get a file not found error. I've confirmed that it's not protection on the PDF and not something with the file name.

I found another ASP script that looked like it turned off buffering and split the file into 'chunks', but it didn't work at all (even with smaller files).

Any idea what to do?
On Monday, December 21, 2009 7:25:27 PM Imar Spaanjaars said:
Hi Seth,

Not sure, but it sounds like a setting of IIS, the web server. Searching for max file size IIS download on Google or Bing might bring something up...

Cheers,

Imar
On Friday, April 20, 2012 8:16:46 AM Stephen said:
This works inInternet Explorer and Ppera, but not in Chrome or Firefox. strange

the paths are correct, nospaces, correct case all lowercase.
If i do a jpeg it works in all browsers.
when i cahnge to test.ppsx  (powerpoint)  it doesn't work in firefox

if i don't use ado stream and do a direct link it works in firefox

I thought it was iss6 MIME types, but all mime types are added and surely it wouldn't work in Intenet explorer or Opera.

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.