ASP.NET Core MVC Download Files

This article uses ASP.NET Core. If using the .NET Framework, see this article.

When returning files, the FileResult method return type can be used.

The quickest and simplest way to download files is to specify the file name using the virtual path. The MIME Content-Type is also required. Valid values are not limited to "image/jpeg", "image/gif", "image/png", "text/plain", "application/x-zip-compressed" and "application/json".

System.Net.Mime.MediaTypeNames.Application.Json
using Microsoft.AspNetCore.Mvc;

namespace Website.Controllers
{
	public class HomeController : Controller
	{
		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			// this file is in the wwwroot folder
			return File("/test.txt", "text/plain");
		}
	}
}

The other way to download this is to append a content-disposition header. Don't worry, ASP.NET Core makes it simplier than in the past, with MVC 5.

using Microsoft.AspNetCore.Mvc;

namespace Website.Controllers
{
	public class HomeController : Controller
	{
		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			// this will append the content-disposition header and download the file to the computer as "downloaded_file.txt"
			return File("/test.txt", "text/plain", "downloaded_file.txt");
		}
	}
}

Another way is to use a Stream. The amount of code is greatly increased in this example that's doing the same thing as example #1.

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using System.IO;

namespace Website.Controllers
{
	public class HomeController : Controller
	{
		private IHostingEnvironment env { get; }

		public HomeController(IHostingEnvironment env) => this.env = env;

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = env.WebRootPath + "\\test.txt";
			return File(new FileStream(file, FileMode.Open), "text/plain"); // could have specified the downloaded file name again here
		}
	}
}

Yet another way is to read the bytes from the file which requires more memory and is not very efficient when working with files.

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;

namespace Website.Controllers
{
	public class HomeController : Controller
	{
		private IHostingEnvironment env { get; }

		public HomeController(IHostingEnvironment env) => this.env = env;

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = env.WebRootPath + "\\test.txt";
			byte[] data = System.IO.File.ReadAllBytes(file);
			return File(data, "text/plain"); // could have specified the downloaded file name again here
		}
	}
}

Here is an example of downloading a zip file. Notice that the FileStream fs is closed by zip.Close(); so it has to be re-opened.

using ICSharpCode.SharpZipLib.Zip;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;

namespace Website.Controllers
{
	public class HomeController : Controller
	{
		private IHostingEnvironment env { get; }

		public HomeController(IHostingEnvironment env) => this.env = env;

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = env.WebRootPath + "\\temp.zip"; // in production, would probably want to use a GUID as the file name so that it is unique
			System.IO.FileStream fs = System.IO.File.Create(file);
			using (ZipOutputStream zip = new ZipOutputStream(fs))
			{
				byte[] data;
				ZipEntry entry;
				entry = new ZipEntry("downloaded_file.txt");
				entry.DateTime = System.DateTime.Now;
				zip.PutNextEntry(entry);
				data = System.IO.File.ReadAllBytes(env.WebRootPath + "\\test.txt");
				zip.Write(data, 0, data.Length);
				zip.Finish();
				zip.Close();
				fs.Dispose(); // must dispose of it
				fs = System.IO.File.OpenRead(file); // must re-open the zip file
				data = new byte[fs.Length];
				fs.Read(data, 0, data.Length);
				fs.Close();
				System.IO.File.Delete(file);
				return File(data, "application/x-zip-compressed", "downloaded_file.zip"); // recommend specifying the download file name for zips
			}
		}
	}
}

This also works equally as well (using System.Net.Mime.MediaTypeNames for the Content-Type):

using ICSharpCode.SharpZipLib.Zip;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;

namespace Website.Controllers
{
	public class HomeController : Controller
	{
		private IHostingEnvironment env { get; }

		public HomeController(IHostingEnvironment env) => this.env = env;

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = env.WebRootPath + "\\temp.zip"; // in production, would probably want to use a GUID as the file name so that it is unique
			System.IO.FileStream fs = System.IO.File.Create(file);
			using (ZipOutputStream zip = new ZipOutputStream(fs))
			{
				byte[] data;
				ZipEntry entry;
				entry = new ZipEntry("downloaded_file.txt");
				entry.DateTime = System.DateTime.Now;
				zip.PutNextEntry(entry);
				data = System.IO.File.ReadAllBytes(env.WebRootPath + "\\test.txt");
				zip.Write(data, 0, data.Length);
				zip.Finish();
				zip.Close();
				fs.Dispose(); // must dispose of it
				fs = System.IO.File.OpenRead(file); // must re-open the zip file
				data = new byte[fs.Length];
				fs.Read(data, 0, data.Length);
				fs.Close();
				System.IO.File.Delete(file);
				return File(data, System.Net.Mime.MediaTypeNames.Application.Zip, "downloaded_file.zip"); // ZIP
			}
		}
	}
}

As does this:

using ICSharpCode.SharpZipLib.Zip;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;

namespace Website.Controllers
{
	public class HomeController : Controller
	{
		private IHostingEnvironment env { get; }

		public HomeController(IHostingEnvironment env) => this.env = env;

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = env.WebRootPath + "\\temp.zip"; // in production, would probably want to use a GUID as the file name so that it is unique
			System.IO.FileStream fs = System.IO.File.Create(file);
			using (ZipOutputStream zip = new ZipOutputStream(fs))
			{
				byte[] data;
				ZipEntry entry;
				entry = new ZipEntry("downloaded_file.txt");
				entry.DateTime = System.DateTime.Now;
				zip.PutNextEntry(entry);
				data = System.IO.File.ReadAllBytes(env.WebRootPath + "\\test.txt");
				zip.Write(data, 0, data.Length);
				zip.Finish();
				zip.Close();
				fs.Dispose(); // must dispose of it
				fs = System.IO.File.OpenRead(file); // must re-open the zip file
				data = new byte[fs.Length];
				fs.Read(data, 0, data.Length);
				fs.Close();
				System.IO.File.Delete(file);
				return File(data, System.Net.Mime.MediaTypeNames.Application.Octet, "downloaded_file.zip"); // OCTET
			}
		}
	}
}

Or read bytes from a MemoryStream:

using Microsoft.AspNetCore.Mvc;

namespace Website.Controllers
{
	public class HomeController : Controller
	{
		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			using (var ms = new System.IO.MemoryStream())
			{
				ms.WriteByte((byte)'a');
				ms.WriteByte((byte)'b');
				ms.WriteByte((byte)'c');
				byte[] data = ms.ToArray();
				return File(data, "text/plain", "downloaded_file.txt");
			}
		}
	}
}
Feedback
close