You should use the Directory FileInfo classes to collect and present the names of the files you want to make available for download to the end user. You can 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. The next picture presents this functionality.
In the code-behind class for the page, use the .NET language to:
1. Collect information about available files to download, by using the GetFiles method of the Directory class.
2. Fill in the ListBox with the filenames by biding the list of files to the ListBox.
3. Process the Download button click event and stream the selected file to the browser using the Response object.
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:
1. Your code has to collect and present to the user a list of the available files that can be downloaded along with a button to initiate the download.
2. You code has to process the button click event and stream the selected file to the browser.
In the a ListBox is used 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 C:\Downloads directory. You can use dedicated directory on the Web server instead.
The ListBox is populated by using GetFiles method of the Directory class to collect 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 your code needs to remove the path information for each file to simplify the list you present to the user.
Next, you should bind the files array to the ListBox and select the first entry in the list.
When the user clicks the Download button to initiate the download, the btnDownload_Click method in the code-behind executes. This routine calls another routine BufferedFileDownload.
The routine BufferedFileDownload does the main tasks. It checks if the file exists. To stream a file to a browser it 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, you should 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 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.
Your file will be read block after block of bytes specified by the variable bufsz. By default bufsz = 4096, but if your file is smaller you use its length as bufsz. When you read one block you are ready to write it to the file to the Response object using Response.BinaryWrite. Finally you read and write the rest of bytes. When the operation is complete you check if bytes transferred are equal to the length of the file.
The next lines show markup in .aspx file in our case TestDownloadVC.aspx
<%@ Page Language=”vb” AutoEventWireup=”true” CodeBehind=”TestDownloadVC.aspx.vb” Inherits=”FileDownloadVC.TestDownloadVC” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”https://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title></title>
</head>
<body style=”height: 22px”>
<form id=”form1″ runat=”server”>
<div align=”center”>
<asp:Label ID=”lblTitle” runat=”server” Font-Bold=”True” Text=”Select file and press Download”></asp:Label>
<br />
<asp:ListBox ID=”FilesList” runat=”server” Height=”162px” Width=”226px”> </asp:ListBox>
<br />
<asp:Button ID=”btnDownload” runat=”server” onclick=”btnDownload_Click” Text=”Download” />
</div>
</form>
</body>
</html>
The next lines present code-behind in VB.NET
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.IO
Public Class TestDownloadVB
Inherits System.Web.UI.Page
‘
‘ The next routine provides the event handler for the page load event.
‘ It is responsible for initializing the controls on the page.
‘
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim files() As String
Dim idx As Integer
‘ Set up the download button event handler
If (Not Page.IsPostBack) Then
‘ Get the list of files in the specified directory
‘ Note: If you want to use specific directory from the Web server replace the next row with
‘ files = Directory.GetFiles(Server.MapPath(“NameOfDirectory”));
‘ where the NameOfDirectory is the place where you keep files for downloading
files = Directory.GetFiles(“C:\\Downloads\\”)
‘ Remove the path to the files
For idx = 0 To files.Length – 1
files(idx) = New FileInfo(files(idx)).Name
Next
‘ Bind the list of files to the listbox on the form
FilesList.DataSource = files
FilesList.DataBind()
‘ Select the first entry in the list
FilesList.SelectedIndex = 0
End If
End Sub
Protected Sub btnDownload_Click(sender As Object, e As EventArgs) Handles btnDownload.Click
If (Not BufferedDownload()) Then
System.Web.UI.ScriptManager.RegisterStartupScript(Me, Me.GetType(), “FileNotDownloadedWarning”, “alert(‘File was not downloaded!’)”, True)
End If
End Sub
‘
‘ The next routine processes file selected for download.
‘ It returs true if the chosen file is processed without errors.
‘ Internally it does the following steps:
‘ 1. Checks if file exists
‘ 2. Specifies the size of buffer.
‘ 3. Calculates the number of blocks of bytes which will be buffered and rest of bytes if they exist
‘ 4. Does buffered transfer
‘
Protected Function BufferedDownload() As Boolean
Dim file As FileInfo
Dim filename As String
Dim fromFile As FileStream
Dim binReader As BinaryReader
‘ Get the fully qualified name of the selected file
‘ Note: If you have used specific directory from the Web server you have to replace
‘ the next row with
‘ filename = Directory.GetFiles(Server.MapPath(“NameOfDirectory”)) & “\” & FilesList.SelectedItem.Text;
‘ where the NameOfDirectory is the place where you keep files for downloading
filename = “C:\Downloads\” & FilesList.SelectedItem.Text
‘ Get the file data
file = New FileInfo(filename)
If (Not file.Exists) Then
System.Web.UI.ScriptManager.RegisterStartupScript(Me, Me.GetType(), “FileNotFoundWarning”, “alert(‘File is not available now!’)”, True)
BufferedDownload = False
End If
Try
‘ Clear the content of the responce
Response.Clear()
Response.AddHeader(“Content-Disposition”, “attachment; filename=” + FilesList.SelectedItem.Text)
Response.AddHeader(“Content-Length”, file.Length.ToString())
‘ Set the content type
Response.ContentType = “application/octet-stream”
‘ Buffered download in blocks of 4096 bytes
Dim bufsz As Integer = 0 ‘ size of buffer
Dim block As Long = 1 ‘ block of bytes
Dim blocks As Long = 0 ‘ number of blocks of bytes
Dim rest As Integer = 0 ‘ number of bytes after the final block of bytes possible values are from 0 to size of buffer-1
‘ In the next if…else clause are determined
‘ size of buffer
‘ number of blocks
‘ rest of bytes after the final block of bytes
If (file.Length < 4096) Then
bufsz = CInt(file.Length)
blocks = 1
Else
bufsz = 4096
blocks = file.Length / bufsz
rest = CInt(file.Length Mod bufsz)
End If
fromFile = New FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
binReader = New BinaryReader(fromFile)
While ((block <= blocks) And (Response.IsClientConnected))
Response.BinaryWrite(binReader.ReadBytes(bufsz))
Response.Flush()
block = block + 1
End While
Response.BinaryWrite(binReader.ReadBytes(rest))
If (block * bufsz + rest = file.Length) Then
Response.End()
‘ Close BinaryReader and FileStream
binReader.Close()
fromFile.Close()
BufferedDownload = True
End If
BufferedDownload = False
Catch ex As Exception
Response.End()
‘ Close BinaryReader and FileStream
binReader.Close()
fromFile.Close()
BufferedDownload = False
Finally
Response.End()
‘ Close BinaryReader and FileStream
binReader.Close()
fromFile.Close()
End Try
End Function
End Class