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:
- Create a folder called /Downloads
- Move the download file for this article (DownloadFile.asp) to the new Downloads folder and rename it as Default.asp
- 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