Community Pick: Many members of our community have endorsed this article.

Save as - for any file type

Published:
Updated:
When developing different types of ASP.NET applications I often face a requirement to let user download different types of files. They vary from logs in *.txt files or manuals/instructions in *.pdf files to charts in *.jpg files or reports in *.xls files. In most cases these files are generated at run-time or stored in a folder not directly accessible to user through URL.
As you know clicking on a link referencing e.g. *.txt file opens this file in browser window. Sometimes this behavior is not very comfortable for user when he/she desires to save file for later use.

This article will show you how to set up your application and links in your pages so user would be provided with Save As& dialog when clicking on such link.

Note: I use Visual Studio 2008 and C# language in this article and in a demo application available to download at the end of the article.

1. Create folder for you files


Create a folder where you will put files user could download. If files should not be accessible directly by URL to user App_Data folder (or subfolder in App_Data folder) would be a good choice because content from App_Data folder is not served to user.

To create a folder:
  1. In Visual Studio right-click on project name in solution explorer
  2. Choose New Folder
  3. Type a desired name of a folder. (I will use Files in this article.)
Create new folder
By default App_Data folder is automatically created when you create a new Web Site. If not (or you deleted one):
  1. In Visual Studio right-click on project name in solution explorer
  2. Choose Add ASP.NET Folder > App_Data.
Create App_Data folderFolder will be created and named for you.

2. Put files in created folder


Put any files you want to server to user in a newly created folder.

3. Create a HTTP handler to serve files.


From MSDN:

An ASP.NET HTTP handler is the process (frequently referred to as the "endpoint") that runs in response to a request made to an ASP.NET Web application. The most common handler is an ASP.NET page handler that processes .aspx files. When users request an .aspx file, the request is processed by the page through the page handler. You can create your own HTTP handlers that render custom output to the browser.
Basically HTTP handlers are special types of ASP.NET files where you provide content to user by manipulating HttpResponse object. Though the same functionality can be achieved with regular aspx page in this scenario HTTP handler is more suitable because it HTTP handlers are more lightweight and it takes less time for ASP.NET engine to process them.

To create HTTP handler:
  1. In Visual Studio right-click on project name in solution explorer
  2. Choose Add New Item&
  3. In open dialog choose Generic Handler from templates list
  4. Type desired handler's name in Name textbox (I will use "FileDownload.ashx" in this article)
  5. Click Add button
  Create HTTP handler
Note: handler name will be used in links on pages so make sure you give your handler a meaningful name

4. Make your handler work for you and users of your application


To do this you will have to implement two members of IHttpHandler interface. Let's start from easy one.

::: IsReusable property :::
From MSDN:

The IsReusable property specifies whether the IHttpHandlerFactory object (the object that actually calls the appropriate handler) can put the handler in a pool and reuse it to increase performance. If the handler cannot be pooled, the factory must create a new instance of the handler every time that the handler is needed.

public bool IsReusable
                      {
                      	get
                      	{
                      		return false;
                      	}
                      }

Open in new window


By default when you create a HTTP handler this property returns false. As handler we are creating is not resource intensive we could leave it as is. I've read forums where people complain that returning true has a negative impact on handler performance. I've tried returning true in my handlers but did not notice any effect so I always do return false;.

::: ProcessRequest method :::
From MSDN:

The ProcessRequest method is responsible for processing individual HTTP requests. In this method, you write the code that produces the output for the handler.
HTTP handlers have access to the application context. This includes the requesting user's identity (if known), application state, and session information. When an HTTP handler is requested, ASP.NET calls the ProcessRequest method of the appropriate handler. The code that you write in the handler's ProcessRequest method creates a response, which is sent back to the requesting browser.

As everything said is quite clear let's proceed further. HTTP handlers (I mean *.ashx files) are requested by browsers and support query strings as well. We are going to use query string to know witch file to serve.

//...
                      // Add necessary namespace to the top
                      // as we are working with file system
                      using System.IO;

Open in new window


public void ProcessRequest(HttpContext context)
                      {
                      	// You should validate query string here to make sure
                      	// it contains necessary information
                      
                      	if (context.Request.QueryString.Count <= 0)
                      	{
                      		// could redirect to error page here
                      	}
                      
                      	string file = context.Request.QueryString["FileName"];
                      
                      	if (string.IsNullOrEmpty(file))
                      	{
                      		// could redirect to error page here
                      	}
                      
                      
                      	// Combine file name with folder name
                      	string filePath = context.Server.MapPath(string.Concat("~/Files/" + file));
                      
                      	// Check if requested file exists
                      	if (!File.Exists(filePath))
                      	{
                      		// could redirect to error page here
                      	}
                      
                      	// Always nice to know you're working with clear response object
                      	// just in case
                      	context.Response.Clear();
                      
                      	// This is the heart of your functionality. Browser read response's content type
                      	// and decide whether they should show Save As.. dialog or show response in
                      	// in it's window. Special value application/save forces browser to show Save As... dialog
                      	context.Response.ContentType = "application/save";
                      
                      	// Browser use this to detect name of a file to be downloaded
                      	context.Response.AddHeader("Content-Disposition", "attachment; filename=" + file);
                      
                      	// Add file itself to response
                      	context.Response.WriteFile(filePath);
                      
                      	// Flush our rendered response to browser
                      	context.Response.Flush();
                      
                      	// We are done
                      	context.Response.End();
                      
                      }

Open in new window


5. Links to files


As we have finished setting up our handler let's proceed to links.
Suppose we have a file named template.txt in our Files folder and we want to provide user with link so he/she could download that file by clicking on this link.
You can use either ASP.NET Hyperlink control or standard HTML <a> element to create link. The only difference is that ASP.NET Hyperlink control accepts application relative URLs (e.g."~/Files/template.txt") and standard HTML <a> elements do not.

<asp:HyperLink runat="server" NavigateUrl="~/FileDownload.ashx?FileName=template.txt" Text="Download template" />
                      <br />
                      <a href="FileDownload.ashx?FileName=template.txt">Download template</a>

Open in new window


Clicking on link like this user will be provided with "Save As&" dialog to save file instead of showing in browser window.

This is it. Our goal achieved.
This is just a simple sample. You can develop a more advances handlers where you pass some ID of database record to handler and it outputs some report in *.pdf file or chart in *.jpg file or& depends on requirements and needs.

In a demo application I attached to article you will find more extensive scenarios of FileDownload HTTP handler. EE does not allow uploading most of ASP.NET files (even if they are compressed in zip file) so I added a .txt extension to every file.
Download here: Demo.zip

This is my first article ever so please forgive me if did not like something about style of writing.
Your comments and criticism are very welcome.

Resources:
  HTTP Handlers and HTTP Modules Overview
  http://msdn.microsoft.com/en-us/library/bb398986.aspx

  Walkthrough: Creating a Synchronous HTTP Handler
  http://msdn.microsoft.com/en-us/library/ms228090.aspx

  IHttpHandler Interface
  http://msdn.microsoft.com/en-us/library/system.web.ihttphandler.aspx
3
7,371 Views

Comments (2)

Commented:
This is the best explanation of asp.net filedownload I've seen - after months of searching this worked for me within 15 minutes. Absolutely 1st class.

A HUGE THANKS for this post.

Author

Commented:
You're welcome
I'm very glad it helped you

R

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.