Download Files in ASP.NET MVC 3 using Controller Action

Posted by: Mahesh Sabnis , on 5/10/2012, in Category ASP.NET MVC
Views: 171895
Abstract: The FileResult action that comes out of box with ASP.NET MVC3 provides an easy to use abstraction for downloading files in an ASP.NET MVC application.

Last week I was conducting a training for one of my clients on ASP.NET MVC 3 features. They had a file server, hosting various types of reports and were using an ASP.NET Web Form application as a front-end to download the reports on the client machine. Now since they planned on migrating the ASP.NET WebForms to ASP.NET MVC, they expected a similar functionality in MVC too.

As most of you working on ASP.NET MVC know, the Views are directly controlled by controller action. Every action method from the controller class returns an ActionResult. This itself is an abstract class. We have several classes inherited from it and they are used in specific cases. One of the classes inherited from ActionResult is FileResult. This class is used to send binary file content to the response. In the following sample, we will see how we can leverage the FileResult action to download files in an ASP.NET MVC Web Application.

 

Step 1: Open VS2010 and create a new ASP.NET MVC 3 project, name it as ‘MVC3_Returning_Files’.

Step 2: In the project, add a new folder and name it as ‘Files’. Add couple of PDF files in it.

Step 3: Right-click on the Models folder and add a new class file, name it as ‘DataClasses.cs’. Add the following classes in it:

namespace MVC3_Returning_Files.Models
{
    public class DataClasses
    {
        public List<FileNames> GetFiles()
        {
           
            List<FileNames> lstFiles = new List<FileNames>();
            DirectoryInfo dirInfo = new DirectoryInfo(HostingEnvironment.MapPath("~/Files"));
           
            int i = 0;
            foreach (var item in dirInfo.GetFiles())
            {

                lstFiles.Add(new FileNames() {

                FileId = i + 1, FileName = item.Name, FilePath = dirInfo.FullName+@"\"+item.Name});
                i = i + 1;
            }
           
            return lstFiles;
        }
    }

    public class FileNames
    {
        public int FileId { get; set; }
        public string FileName { get; set; }
        public string FilePath { get; set; }
    }
}

The class DataClasses contains ‘GetFiles’ method. This method reads all files from the ‘Files’ folder created in Step 2 and returns a list of files with information as Field, FileName and FilePath declared in FileNames class.

Step 4: Add a new ‘ReportsController’ with the following action methods:

namespace MVC3_Returning_Files.Controllers
{
    public class ReportsController : Controller
    {
        DataClasses objData;

        public ReportsController()
        {
            objData = new DataClasses();
        }

        //
        // GET: /Reports/

        public ActionResult Index()
        {
            var files = objData.GetFiles(); 
            return View(files);
        }

      
        public FileResult Download(string id)
        {
            int fid = Convert.ToInt32(id);
            var files = objData.GetFiles();
            string filename = (from f in files
                               where f.FileId == fid
                               select f.FilePath).First();
            string contentType = "application/pdf";
            //Parameters to file are
            //1. The File Path on the File Server
            //2. The content type MIME type
            //3. The parameter for the file save by the browser
            return File(filename, contentType,"Report.pdf");
        }

    }
}

 

The ‘Download’ action method accepts id (FileId) from the view and queries the Files List returned from the GetFiles method. After querying the List of files, it retrieve the file path. It also defines content type which is MIME type defined content header. This information is required by the browser to decide how to handle file. In this case, it is defined as  aPDF. The method returns an object of the type ‘FilePathResult’. This is the class inherited from FileResult. FilePathResult accepts three parameters. The first is the file path of the file which is to be downloaded. The second is the content type. The third parameter is the Download file name. This is an optional parameter, but if passed, then the browser will show the download effect.

Step 5: Add a new Index View using Index action which has some Razor code as shown below:

model IEnumerable<MVC3_Returning_Files.Models.FileNames>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
     <th>
            FileId
        </th>
        <th>
            FileName
        </th>
    </tr>

@foreach (var item in Model) {
    <tr>
    <td>
            @Html.DisplayFor(modelItem => item.FileId)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.FileName)
        </td>
        <td>
            @Html.ActionLink("Download", "Download", new { id = item.FileId })
         
        </td>
    </tr>
}

</table>

 

Step 6: Add the following Action link in the menucontainer <div> of the _Layout.cshtml:

<li>@Html.ActionLink("Reports", "Index", "Reports")</li>

Step 7: Run the application and you will find the Report menu. After clicking on it, the Index page will display all the Files as shown below:

MVC 3 Download File

Click on the Download link and you will get a download box as shown below (Note: I am using IE9)

image

Conclusion

The FileResult action that comes out of box with ASP.NET MVC3 provides an easy to use abstraction for downloading files in an ASP.NET MVC application.

The entire source code of this article can be downloaded from GitHub

Give a +1 to this article if you think it was well written. Thanks!
Recommended Articles


Page copy protected against web site content infringement by Copyscape


User Feedback
Comment posted by Naresh on Friday, May 11, 2012 2:32 AM
Niceeeee
Comment posted by mathew on Tuesday, June 12, 2012 1:02 AM
How can i add multiple files to a single zip file and permits the users to download this zip file?
Comment posted by Doctor Benigno on Monday, June 18, 2012 7:10 AM
seems there is a problem in step 5

the view croaks with a CS1963 error , the expression tree cannot contain a dinamic operation.

Seems that the use of the @Html.DisplayFor helper needs something more to run smoothly. If i comment out the uses of the helper , the example runs just fine.

@Html.DisplayFor(modelItem => item.FileId)
and
@Html.DisplayFor(modelItem => item.FileName)

need to be tweaked into something else.
Comment posted by meganathan subramani on Saturday, June 23, 2012 5:36 AM
Hi,

I can upload more than 5MB size files, but i can't able to download that files. Give me the solution.
Thanks,
Comment posted by a on Tuesday, July 3, 2012 5:25 AM
a
Comment posted by Pat M on Monday, July 9, 2012 7:18 PM
Thanks for the great tutorial.  I had been trying to adjust the router to enable downloads of zip files but your method makes a lot more sense.  I appreciate the help.
Comment posted by Triven on Wednesday, July 18, 2012 5:44 AM
Hi Mahesh,

I am getting the following error
The current request for action 'Download' on controller type mycontroller is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Download() on type myproject.Controllers.mycontroller
System.Web.Mvc.FileResult Download(System.String) on type myproject.Controllers.mycontroller

Need help to resolve the same...
Comment posted by Triven on Wednesday, July 18, 2012 6:39 AM
Hi Mahesh,

Just needed to have different names for FileResult and ActionResult
Comment posted by Triven on Wednesday, July 18, 2012 6:42 AM
Hi Mahesh,

First issue is resolved , but what if i want to download the file with it's actual extension.?

Thanks.
Comment posted by G.NAGA HARISH CHOUDARY on Friday, October 5, 2012 7:34 AM
hi sir, iam very happy withur posted  data ,( regarding the uploading, i want to know about how to download  plz  post as soon as possible)

THANKING YOU,
G.N.H.C  
Comment posted by Amit on Tuesday, October 23, 2012 2:06 AM
This is Very use full Post Thanks.
Comment posted by Mahesh on Tuesday, October 23, 2012 3:46 PM
Very very very usefull.. Thanks a lot for the article.. You saved lot of my time.
Comment posted by pradeep gusain on Tuesday, October 30, 2012 2:21 AM
@ Doctor Benigno
use  <td> @item.FileId </td>
     <td> @item.FileName </td>

instead of
  <td>
            @Html.DisplayFor(modelItem => item.FileId)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.FileName)
        </td>
Comment posted by gfgf on Tuesday, February 12, 2013 8:37 AM
fggfg
Comment posted by praveen vyas on Sunday, March 17, 2013 6:47 AM
really nice blog
Comment posted by Dinesh on Friday, June 21, 2013 9:43 AM
Nice one...
One query: Can i Download rather than showing all the files directly.?
Else Can i give an option to download all the files??
Comment posted by sudheer lakki on Thursday, July 11, 2013 10:32 AM
My problem is , i am trying to download a youtube video using URL ,
I am downloading the file in localhost,when i pushed my solution to live i am not able to download,here my code follows
string downloadPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string sFilePath = string.Format(Path.Combine(downloadPath, "{0}.{1}"), videoTitle, videoFormt);
WebClient webClient = new WebClient();
webClient.DownloadFileAsync(new Uri(sURL), sFilePath);

Please tell a solution.

Thanks,
Sudheer Lakki
Comment posted by AntBoots on Wednesday, July 24, 2013 10:27 AM
Thanks Mahesh, great time saving solution for me :-)
Comment posted by Pradeep on Wednesday, August 7, 2013 7:48 AM
It helped a alot.
Comment posted by Sasi Reddy on Thursday, September 12, 2013 6:26 AM
Thank U SOOOOOO Much.
Comment posted by Tanya on Friday, November 8, 2013 2:31 PM
Great one out on web!

One question, please help.

Can we implement the same with small modification:Instead of this on DataClasses.cs:
DirectoryInfo dirInfo = new DirectoryInfo(HostingEnvironment.MapPath("~/Files"));
----Can we have this change dynamically, like for a controller name 'HR' i want the above dirInfo be :
DirectoryInfo dirInfo = new DirectoryInfo(HostingEnvironment.MapPath("~/Files/HR"));
------
I could write a switch statement to do the above but cant understand how the Razor view be modified?

Since this is my first MVC4 app I am really struggling with the syntax issues etc. Any help is appreciated.
Comment posted by irshad on Friday, February 14, 2014 1:03 AM
Hi Mahesh,

If we want to return to view or update view after download, how can we do that?

Here, we are returning "File" directly to browser. So the Ui is not updating.

Plese help.
Comment posted by priya on Thursday, March 13, 2014 3:57 AM

good one...
Comment posted by Lokesh on Monday, April 14, 2014 1:39 AM
Its very Helpful
Comment posted by IQRA on Friday, November 21, 2014 6:43 AM
I have my word template save in my project. i want to use that word template and based on the details on the web form the details have to be saved into that word template and download that template using MVC3.How can i do it ?

Post your comment
Name:  
E-mail: (Will not be displayed)
Comment:
Insert Cancel