Recipe 20.3. Creating a File Download Handler


Problem

You want to create a general-purpose file handler to download files of any type where the name of the file being downloaded is provided at runtime.

Solution

Create an HTTP handler to read the required file from the filesystem and send it to the browser. The steps for creating an HTTP handler are defined in Recipe 20.1.

Examples 20-8 and 20-9 show the VB and C# class files we've written to implement a file download HTTP handler. Examples 20-10, 20-11 through 20-12 show the .aspx file and the VB and C# code-behind files for our application that demonstrates the use of the file download HTTP handler.

Discussion

Of the many ways you might implement a reusable file download routine that can handle almost any file type, creating an HTTP handler makes the most sense. HTTP handlers are designed to process requests for resources, and a file download request is a special instance of such a request.

The file download HTTP handler described in our example downloads a file from the local filesystem to the user's system. The name of the file to download is passed in the URL used to access the HTTP handler.

The first step in implementing the file download HTTP handler is to create a class that implements IHttpHandler. The class can be part of your web project, or if you want it to be reusable across applications, you can place it in a project by itself so a separate assembly can be created, as described in Recipe 20.1.

As discussed in the previous recipe, implementing the IHttpHandler interface requires the implementation of two methods: IsReusable and ProcessRequest. IsReusable is a property that explicitly returns a Boolean value that indicates if the HTTP handler can be reused by other HTTP requests. For synchronous handlers like our example, the property should always return false so the handler is not pooled (kept in memory).

The ProcessRequest method provides all of the functionality required to download the file. The first step is to retrieve the name of the file that is to be downloaded from the URL that is being processed by the handler.

In our example, we provide only the filename in the URL that calls the handler because all files to be downloaded are intended to be located in a single Downloads directory associated with the application. However, because a fully qualified filename is required for the download along with the size of the file, a FileInfo object is created passing the fully qualified name of the file to the constructor. The path to the Downloads directory is obtained using Server.MapPath, which translates a relative path within the web application to a fully qualified path on the web server's filesystem.

Review how you've implemented your file download handler to ensure a hacker cannot download files you do not intend to have downloaded. For example, if you do not restrict downloadable files to a single area, as we have done in our example, a hacker could possibly enter the following to download your web.config file:

http://aspnetcookbook/FileDownloadHandlerVB.aspx?
filename=web.config

By restricting downloadable files to a single area and providing the path information in your code instead of in the URL, you can block a hacker from accessing restricted folders. In addition, you can use filesystem security to improve the security of your application.


To send a file to the browser, you must write it to the Response object. Passing data in the response object is the only way to return data to the browser. Based on the content type (described later), the browser processes the data returned using the Response object.

The first step in writing the data to the Response object is to clear any data in the object because no other data can be included with the file or a corrupted file error will occur.

The AddHeader method of the Response object is then used to add the name of the file being downloaded and its length.

The content type then needs to be set. In our example, it is set to application/octet-stream so it will be treated by the browser as a binary stream and prompt the user to select the location to save the file. For your application, you may want to set the content type to the 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. For more information on content types, consult http://www.w3c.org.

The file is written to the Response object and the response is ended to send the file to the browser.

The web.config file for our application must have an entry in the <httpHandlers> element to tell ASP.NET which URL requests need to be routed to the file download HTTP handler. You use the add element and its attributes to specify each custom handler. The verb attribute of the add element defines the types of requests that are routed to the HTTP handler. The allowable values are *, GET, HEAD, and POST. The value * is a wildcard for all request types.

The path attribute defines the URL(s) that are to be processed by the HTTP handler. The path can be a single URL, as shown later, or it can be set to something like *.download to have the HTTP handler process all requests for URLs with a download extension. (See the note in Recipe 20.1 regarding request extensions.)

The type attribute defines the name of the assembly and class within the assembly that will process the request in the format type="class name, assembly". The class must be identified by its full namespace. The following code shows how to add a reference to the file download handler to the web.config file of our sample application:

 

<configuration> <system.web> … <httpHandlers> <add verb="*" path="FileDownloadHandlerVB.aspx" type="ASPNetCookbook.VBExamples.HttpHandlers.FileDownloadHandlerVB, CH20FileDownloadHandlerVB"/> </httpHandlers>

… </system.web> </configuration>

<configuration> <system.web> … <httpHandlers> <add verb="*" path="FileDownloadHandlerCS.aspx" type="ASPNetCookbook.CSExamples.HttpHandlers.FileDownloadHandlerCS, CH20FileDownloadHandlerCS"/> </httpHandlers> … </system.web> </configuration>

To use the HTTP handler (described earlier) to download files, we need to set the href attribute of an HTML anchor tag to the name of the file download HTTP handler defined in the path attribute of the entry added to web.config, passing the name of the file to download in the URL. In our example, the HRef attribute of the anchor tag is set in the Page_Load method of the test page. Here is a sample of the URL:

 href="FileDownloadHandlerVB.aspx?Filename=SampleDownload.txt" 

This example demonstrates downloading a file that exists on the web server. The code can be altered to pass additional data in the URL, programmatically generate the file, and send it to the browser without saving it to the filesystem. This could be useful if you are dynamically creating a PDF or a CSV file requested by the user.

See Also

Recipe 20.1 for techniques on how to create a generic, reusable HTTP handler; http://www.w3c.org for information on content types

Example 20-8. File download HTTP handler (.vb)

 Option Explicit On Option Strict On Imports System Imports System.IO Imports System.Web Namespace ASPNetCookbook.VBExamples.HttpHandlers ''' <summary> ''' This class provides a file download handler as an HTTP handler. ''' </summary> Public Class FileDownloadHandlerVB Implements IHttpHandler 'The following constant is used in the URL used to access this handler to 'define the file to download Public Const QS_FILENAME As String = "Filename" 'the following constant defines the folder containing downloadable files Private Const DOWNLOAD_FOLDER As String = "Downloads" '''*********************************************************************** ''' <summary> ''' This property defines whether another HTTP handler can reuse this ''' instance of the handler. ''' </summary> ''' ''' <returns>False</returns> ''' <remarks> ''' False is always returned since this handler is synchronous and is ''' not pooled. ''' </remarks> Public ReadOnly Property IsReusable() As Boolean _ Implements IHttpHandler.IsReusable Get Return (False) End Get End Property 'IsReusable '''*********************************************************************** ''' <summary> ''' This routine provides the processing for the http request. It is ''' responsible for reading the file from the local filesystem and ''' writing it to the response object. ''' </summary> ''' ''' <param name="context">Set to the current HttpContext</param> Public Sub ProcessRequest(ByVal context As HttpContext) _ Implements IHttpHandler.ProcessRequest Dim file As FileInfo Dim filename As String 'get the filename from the querystring filename = context.Request.QueryString(QS_FILENAME) 'get the file data since the length is required for the download file = New FileInfo(context.Server.MapPath(DOWNLOAD_FOLDER) & "\" & _ filename) 'write it to the browser context.Response.Clear() context.Response.AddHeader("Content-Disposition", _ "attachment; filename=" & filename) context.Response.AddHeader("Content-Length", _  file.Length.ToString()) context.Response.ContentType = "application/octet-stream" context.Response.WriteFile(file.FullName) context.Response.End() End Sub 'ProcessRequest End Class 'FileDownloadHandlerVB End Namespace 

Example 20-9. File download HTTP handler (.cs)

 using System; using System.IO; using System.Web; namespace ASPNetCookbook.CSExamples.HttpHandlers { /// <summary> /// This class provides a file download handler as an HTTP handler. /// </summary> public class FileDownloadHandlerCS : IHttpHandler {  // The following constant is used in the URL used to access this handler  // to define the file to download  public const string QS_FILENAME = "Filename";  //the following constant defines the folder containing downloadable files  private const string DOWNLOAD_FOLDER = "Downloads";  ///***********************************************************************  /// <summary>  /// This property defines whether another HTTP handler can reuse this  /// instance of the handler.  /// </summary>  ///  /// <returns>false</returns>  /// <remarks>  /// false is always returned since this handler is synchronous and is  /// not pooled.  /// </remarks>  public bool IsReusable  { get { return (false); } } // IsReusable ///*********************************************************************** /// <summary> /// This routine provides the processing for the http request. It is /// responsible for reading the file from the local filesystem and /// writing it to the response object. /// </summary> /// /// <param name="context">Set to the current HttpContext</param> public void ProcessRequest(HttpContext context) { FileInfo file = null; string filename = null; // get the filename from the querystring filename = context.Request.QueryString[QS_FILENAME]; // get the file data since the length is required for the download file = new FileInfo(context.Server.MapPath(DOWNLOAD_FOLDER) + "\\" + filename); // write it to the browser context.Response.Clear(); context.Response.AddHeader("Content-Disposition", "attachment; filename=" + filename); context.Response.AddHeader("Content-Length",  file.Length.ToString()); context.Response.ContentType = "application/octet-stream"; context.Response.WriteFile(file.FullName); context.Response.End(); } // ProcessRequest } // FileDownloadHandlerCS } 

Example 20-10. Using the file download HTTP handler (.aspx)

 <%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master" AutoEventWireup="false" CodeFile="CH20TestHTTPFileDownloadHandlerVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH20TestHTTPFileDownloadHandlerVB" Title="Test HTTP File Download Handler" %> <asp:Content  runat="server" ContentPlaceHolder> <div align="center" > HTTP Handler For File Downloads (VB) </div> <div align="center" > <br /> Click the link below to download the sample file using an HTTP Handler <br /> <br /> <a  runat="server" ></a> </div> </asp:Content> 

Example 20-11. Using the file download HTTP handler code-behind (.vb)

 Option Explicit On Option Strict On Imports ASPNetCookbook.VBExamples.HttpHandlers Namespace ASPNetCookbook.VBExamples ''' <summary> ''' This class provides the code-behind for ''' CH20TestHTTPFileDownloadHandlerVB.aspx ''' </summary> Partial Class CH20TestHTTPFileDownloadHandlerVB Inherits System.Web.UI.Page '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the page load event. It ''' is responsible for initializing the controls on the page. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Private Sub Page_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load Const FILE_TO_DOWNLOAD As String = "SampleDownload.txt" 'set the text and href of the download anchor anDownload.InnerText = FILE_TO_DOWNLOAD anDownload.HRef = "FileDownloadHandlerVB.aspx?" & _ FileDownloadHandlerVB.QS_FILENAME & "=" & _ FILE_TO_DOWNLOAD End Sub 'Page_Load End Class 'CH20TestHTTPFileDownloadHandlerVB End Namespace 

Example 20-12. Using the file download HTTP handler code-behind (.cs)

 using ASPNetCookbook.CSExamples.HttpHandlers; using System; namespace ASPNetCookbook.CSExamples { /// <summary> /// This class provides the code-behind for /// CH20TestHTTPFileDownloadHandlerCS.aspx /// </summary> public partial class CH20TestHTTPFileDownloadHandlerCS : System.Web.UI.Page { ///*********************************************************************** /// <summary> /// This routine provides the event handler for the page load event. /// It is responsible for initializing the controls on the page. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void Page_Load(object sender, EventArgs e) { const string FILE_TO_DOWNLOAD = "SampleDownload.txt"; // set the text and href of the download anchor anDownload.InnerText = FILE_TO_DOWNLOAD; anDownload.HRef = "FileDownloadHandlerCS.aspx?" + FileDownloadHandlerCS.QS_FILENAME + "=" + FILE_TO_DOWNLOAD; } // Page_Load } // CH20TestHTTPFileDownloadHandlerCS } 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2003
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net