Published: 11 Jul 2011
By: Justin Schwartzenberger
Download Sample Code

Learn how to trigger the browser to download file data via as ASP.NET MVC 3 controller action.

Contents [hide]

Introduction

Providing file download capability in a web application is not as clear cut as linking to a file in the file system of the application. Web browsers have a mind of their own when it comes to handling files. For example, clicking on a link to an image file results in the browser opening up that image in the browser window. So how do you gain control over that workflow and get the web browser to conform to your desired action in an ASP.NET MVC 3 application?

To craft a sample solution for controlling the file download process in a MVC 3 application we are going to need some test files, some helper code for working with the file data, a basic controller and corresponding view to render links to the files, and a controller to provide the file downloads.

Working with the files

We begin by creating a sub directory named Files under the Content directory of the solution where we will place some sample files. To validate that the solution can support some common file types, the Files directory will contain a PDF, a TXT, a JPG, and a CSV file (available in the sample code download).

  • BrownieRecipe.pdf
  • Colors.txt
  • Dog.jpg
  • Teams.csv

The application will need to find these files based on a requested file name and read the file data into a byte array that can be delivered to the response stream. An extension method can be created to handle the logic for finding the file and getting the data. A new class file named ExtensionMethods.cs will reside in the Models directory and will contain a method named GetFileData.

Listing 1: ExtensionMethods.cs

This method takes in the file name and path value, builds the full file path, and if the file is found it will read the file bytes and returns the array. If the file does not exist it throws a new System.IO.FileNotFoundException. The application will need to use the HttpServerUtilityBase.MapPath method to generate the base path to the files in the Content/Files directory in order for the System.IO.File class to access the file in the file system. Since an instance of the HttpServerUtilityBase class is made available via the Server property in the System.Web.Mvc.Controller class, the application will need to generate the base file path in the controller action logic and pass it in as an argument to the extension method so that the extension method will not be dependent on the Server object. While the code in this extension method could be placed directly in the action method, it is handy to encapsulate it in this method so it can easily be reused in other controller actions across the application (or even reused in other applications). This extension method will be able to be called off of a string variable that contains the name of a file.

To trigger a file download in a user agent (web browser) the HTTP response needs to contain header information indicating how the user agent should treat the content. The Content-Disposition needs to be set to "attachment" and given a "filename" value. The Content-Type needs to be set to "application/force-download". Finally, the file data needs to be written to the HTTP response stream. This logic can be handled by creating a class that inherits from the System.Web.Mvc.ContentResult class and returning an instance of that class from a controller action. The name of this class will be FileDownloadResult and it will be placed in the Models directory.

Listing 2: FileDownloadResult.cs

The class has a single constructor that takes in the file name and data values since it will need both values to deliver the file data to the response stream. The ExecuteResult method overrides the base ContextResult method and builds out the response context as needed. Prior to building out the response it verifies that it has values for the file name and the file data and throws some basic exceptions if not.

Controller and actions

The main controller that handles the file download logic is called FilesContoller. It has a single action method to take in a requested file name, call the extension method to get the byte array file data, and return an instance of FileDownloadResult. The code to get the file data and return the result is wrapped in a try/catch block, and in the case of a FileNotFoundException the code will throw a System.Web.HttpException to trigger a 404 status code that could then be handled by a common HTTP error architecture.

Listing 3: FilesController.cs

The application also needs a simple HomeController to provide the initial page with links to the file downloads. This class has an action method named Index that returns the view that will contain the links.

Listing 4: HomeController.cs

The view

The view (Views/Home/Index.cshtml) that the HomeController.Index method uses is fairly trivial. It consists of links to the files that are rendered using the HtmlHelper.ActionLink extension method.

Listing 5: Index.cshtml

These links could be placed in any view in the application.

Routes

The HtmlHelper.ActionLink helper relies upon the routing table to craft the correct URL syntax to the requested action. Without a route the helper will render the links in the following format:

The file variable will get appended on as a GET URL query string parameter. Adding the following route to the route table will instruct the helper on how to format the URL.

With the addition of the route, everything is in place and the file download solution is complete. Running the application and clicking on the links to the files in the web browser will result in a prompt to save or open the file. The same action will occur for all of the file types, including the image (JPG) file.

Taking the next step

Putting the control of the file delivery in ASP.NET MVC 3 controller actions instead of direct linking to files provides the ability to do many interesting server side things. For instance, the same code to package up the file data and deliver it via a ContentResult object can be used on file data from any source, not just a read of a file in the file system. File data can be pulled from a data source record and fed straight to the response stream. Data displayed in a table can have an action method that will query the same data, write the data to a string in a CSV format, convert it to a byte array, and then send that stream to the code that generates the ContentResult to offer users the ability to export data to a CSV file. New solutions are only a download away.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Justin Schwartzenberger

I have been entrenched in web application development for quite a while and have traversed the syntactic jungles of PHP, classic ASP, Visual Basic, VB.NET, and ASP.NET Web Forms. However, I have found a guilty pleasure in ASP.NET MVC since its beta launch and have since refactored my web stack focus...

This author has published 3 articles on DotNetSlackers. View other articles or the complete profile here.

Other articles in this category


Code First Approach using Entity Framework 4.1, Inversion of Control, Unity Framework, Repository and Unit of Work Patterns, and MVC3 Razor View
A detailed introduction about the code first approach using Entity Framework 4.1, Inversion of Contr...
jQuery Mobile ListView
In this article, we're going to look at what JQuery Mobile uses to represent lists, and how capable ...
Exception Handling and .Net (A practical approach)
Error Handling has always been crucial for an application in a number of ways. It may affect the exe...
JQuery Mobile Widgets Overview
An overview of widgets in jQuery Mobile.
Book Review: SignalR: Real-time Application Development
A book review of SignalR by Simone.

You might also be interested in the following related blog posts


Using T4MVC strongly-typed helpers with Telerik Extensions for ASP.NET MVC read more
ASP.NET MVC2 Preview 2: Areas and Routes read more
How to use Ninject 2 with ASP.NET MVC read more
How to download Internet Explorer 8 for Windows 7 E without any Web browser? read more
The ASP.NET MVC ActionController The controllerless action, or actionless controller read more
Going Controller-less in MVC: The Way Fowler Meant It To Be read more
Using the RadFileExplorer for ASP.NET AJAX in a MOSS web application read more
ASP.NET MVC 2.0 and VS 2010 plan now public read more
An Alternative Approach To Strongly Typed Helpers read more
ViewModel with MVC/Navigation in Silverlight read more
Top
 
 
 

Discussion


Subject Author Date
placeholder FileDownloadResult refactor opportunity Justin Schwartzenberger 7/11/2011 3:25 PM
Still reinvent wheel Vincent Yang 7/11/2011 7:56 PM
placeholder RE: Still reinvent wheel Vincent Yang 7/11/2011 7:59 PM
KKBruce KK Bruce 8/31/2011 4:29 AM

Please login to rate or to leave a comment.