Use the Directory
and FileInfo
classes to
gather and present
the names of the files you want to make available for download to the
user. Display their names in a listbox with a button to initiate the
download. When the user clicks the button, stream the selected file
to the browser.
In the .aspx
file, add a listbox and a Download
(or equivalently named) button.
In the code-behind class for the page, use the .NET language of your choice to:
Create a list of available files to download using the
GetFiles
method of the
Directory
class.
Populate the ListBox
with the filenames by binding
the list of files to the ListBox
.
Process the Download button click event and stream the selected file
to the browser using the Response
object.
Example 15-1 through Example 15-3 show the .aspx
file and
VB and C# code-behind files for an application that illustrates our
solution by populating the listbox with a list of files located in
the application’s images directory. The populated
listbox is shown in Figure 15-1, and the prompt that
is output when the user selects a file and clicks Download is shown
in Figure 15-2.
Downloading a file to the browser for display, storage, or printing is a common requirement of a web application. PDF and Word files are perhaps the most ubiquitous download files types, although image, audio, video, and text files are quite common as well.
Downloading a file from a server is a two-step process. The first step is to gather and present to the user a list of the available files that can be downloaded along with a button to initiate the download. The second step is to process the button click event and stream the selected file to the browser.
In our example, we use a listbox to present a list of available files
to the user. The list is populated in the
Page_Load
method of the code-behind with a list of
files located in the images directory of the application. (In a
production application, you may want to create a download folder
instead.)
To populate the listbox, we use the GetFiles
method of the Directory
class to
gather the fully qualified filenames of the files in the specified
folder and return them as an array. The GetFiles
method returns a fully qualified filename for each file it finds, so
our code needs to remove the path information for each file to
simplify the list we present to the user.
Next, we bind the files
array to the listbox and
select the first entry in the list.
Selecting the first entry in the list simplifies the code because no validation is required if we can always assume an item is selected.
When the user clicks the Download button to initiate the download,
the btnDownload_ServerClick
method in the
code-behind executes. In this routine, we use the
MapPath
method of the Server
class to create a fully qualified filename, which we use to
instantiate a FileInfo
object to provide easy
access to the length of the file that is needed for the download.
To stream a file to a browser, you must write it to the
Response
object. The first step in writing a file
to the Response
object is to call its
Clear
method to remove any data currently in the
buffer stream. If the Response
object already
contains data, when you attempt to write a file to it, you will
receive a corrupt file error.
Before writing the file, use the AddHeader
method
of the Response
object to add the name of the file
being downloaded and its length to the output stream. You must also
use the ContentType
method to specify the content
type of the file. In this example, the type is set to
application/octet-stream
so that the browser will
treat the output stream as a binary stream and prompt the user to
select a location to which to save the file. In your own application
you may want to set the content type to an explicit file type, such
as application/PDF
or
application/msword
. Setting the content type to
the explicit file type allows the browser to open it with the
application defined to handle the specified file type on the client
machine.
Now, at last, you are ready to write the file to the
Response
object using
Response.WriteFile
. When the operation is
complete, call Response.End
to send the file to
the browser.
Any code that appears after the Response.End
statement will not be executed. The Response.End
statement sends all buffered data in the Response
object to the client, stops the execution of the page, and raises the
Application_EndRequest
event. For more information
on the application behavior when calling
Response.End
, refer to Knowledge Base article
KB312629.
The only data that can be included in the Response
stream is the file to download. If any other data is included, the
downloaded file will be corrupted. This may occur if your application
uses the Application_EndRequest
method to append a
footer to all pages.
In our example, a list of files currently residing on the filesystem
is presented to the user to select from and download. If your
application is going to dynamically create the file, it is not
necessary to present a list or to save the file to the filesystem.
Instead, remove the listbox and add whatever controls are needed to
collect the information you need to dynamically create the file. When
the user clicks Download, create your file and generate a byte array
containing the file data. Instead of using the
WriteFile
method of the
Response
object, use the
BinaryWrite
method, as shown here:
Response.BinaryWrite([your byte array]
) Response.BinaryWrite([your byte array]
);
For another approach to implementing the solution described in this example using an HTTP handler, refer to Recipe 17.2.
Recipe 17.2 for implementing file downloads
with an HTTP handler; Knowledge Base article KB312629 for more
information on the system operation when
Response.End
,
Response.Redirect
, and
Server.Transfer
are called
Example 15-1. Downloading a file (.aspx)
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH15FileDownloadVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH15FileDownloadVB" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>File Download</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmDownload" method="post" runat="server"> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td align="center"> </td> </tr> <tr> <td align="center" class="PageHeading"> File Download (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center"><asp:ListBox ID="lstFiles" Runat="server" Rows="6" />
</td> </tr> <tr> <td align="center"> <br /><input id="btnDownload" runat="server"
type="button" value="Download">
</td> </tr> </table> </form> </body> </html>
Example 15-2. Downloading a file code-behind (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH15FileDownloadVB.aspx.vb ' ' Description: This module provides the code behind for the ' CH15FileDownloadVB.aspx page ' '***************************************************************************** Imports System Imports System.IO Imports System.Web.UI Namespace ASPNetCookbook.VBExamples Public Class CH15FileDownloadVB Inherits System.Web.UI.Page 'controls on the form Protected lstFiles As System.Web.UI.WebControls.ListBox Protected WithEvents btnDownload As HtmlControls.HtmlInputButton '************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim files( ) As String Dim index As Integer If (Not Page.IsPostBack) Then'get list of files in the images directory (just for example here)
files = Directory.GetFiles(Server.MapPath("images"))
'for display purposes, remove the path to the file
For index = 0 To files.Length - 1
files(index) = New FileInfo(files(index)).Name
Next index
'bind the list of files to the listbox on the form
lstFiles.DataSource = files
lstFiles.DataBind( )
'select the first entry in the list
'NOTE: This is done to simplify the example since preselecting an
' item eliminates the need to verify an item was selected
lstFiles.SelectedIndex = 0
End If End Sub 'Page_Load '*************************************************************************** ' ' ROUTINE: btnDownload_ServerClick ' ' DESCRIPTION: This routine provides the event handler for the download ' button click event. It is responsible for reading the ' selected file from the file system and streaming it to ' the browser. '---------------------------------------------------------------------------Private Sub btnDownload_ServerClick(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnDownload.ServerClick
Dim file As FileInfo
Dim filename As String
'get the fully qualified name of the selected file
filename = Server.MapPath("images") & "" & _
lstFiles.SelectedItem.Text
'get the file data since the length is required for the download
file = New FileInfo(filename)
'write it to the browser
Response.Clear( )
Response.AddHeader("Content-Disposition", _
"attachment; filename=" & lstFiles.SelectedItem.Text)
Response.AddHeader("Content-Length", _
file.Length.ToString( ))
Response.ContentType = "application/octet-stream"
Response.WriteFile(filename)
Response.End( )
End Sub 'btnDownload_ServerClick
End Class 'CH15FileDownloadVB End Namespace
Example 15-3. Downloading a file code-behind (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH15FileDownloadCS.aspx.cs // // Description: This module provides the code behind for the // CH15FileDownloadCS.aspx page // //**************************************************************************** using System; using System.IO; namespace ASPNetCookbook.CSExamples { public class CH15FileDownloadCS : System.Web.UI.Page { // controls on the form protected System.Web.UI.WebControls.ListBox lstFiles; protected System.Web.UI.HtmlControls.HtmlInputButton btnDownload; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) { string[] files = null; int index; // wire the download button event handler this.btnDownload.ServerClick += new EventHandler(this.btnDownload_ServerClick); if (!Page.IsPostBack) {// get list of files in the images directory
files = Directory.GetFiles(Server.MapPath("images"));
// for display purposes, remove the path to the file
for (index = 0; index < files.Length; index++)
{
files[index] = new FileInfo(files[index]).Name;
}
// bind the list of files to the listbox on the form
lstFiles.DataSource = files;
lstFiles.DataBind( );
// select the first entry in the list
// NOTE: This is done to simplify the example since preselecting an
// item eliminates the need to verify an item was selected
lstFiles.SelectedIndex = 0;
} } // Page_Load //************************************************************************ // // ROUTINE: btnDownload_ServerClick // // DESCRIPTION: This routine provides the event handler for the download // button click event. It is responsible for reading the // selected file from the file system and streaming it to // the browser. //------------------------------------------------------------------------private void btnDownload_ServerClick(object sender,
System.EventArgs e)
{
FileInfo file = null;
string filename = null;
// get the fully qualified name of the selected file
filename = Server.MapPath("images") + "\" +
lstFiles.SelectedItem.Text;
// get the file data since the length is required for the download
file = new FileInfo(filename);
// write it to the browser
Response.Clear( );
Response.AddHeader("Content-Disposition",
"attachment; filename=" + lstFiles.SelectedItem.Text);
Response.AddHeader("Content-Length",
file.Length.ToString( ));
Response.ContentType = "application/octet-stream";
Response.WriteFile(filename);
Response.End( );
} // btnDownload_ServerClick
} // CH15FileDownloadCS }