articles » current » asp-net-core » download-files

ASP.NET Core: Download Files

How to download files from an ASP.NET Core Web Application (MVC)

This article uses ASP.NET Core and works for .NET Core 3.1, .NET 5 and .NET 6. If using the .NET Framework, see this article. If wanting to upload a file from the browser to the server, see this article. If wanting to download a file from a URL/URI in a ASP.NET Core application, 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-Type/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 IWebHostEnvironment env { get; }

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

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = System.IO.Path.Combine(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 IWebHostEnvironment env { get; }

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

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = System.IO.Path.Combine(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 IWebHostEnvironment env { get; }

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

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = System.IO.Path.Combine(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(System.IO.Path.Combine(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 IWebHostEnvironment env { get; }

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

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = System.IO.Path.Combine(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(System.IO.Path.Combine(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 IWebHostEnvironment env { get; }

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

		public IActionResult Index()
		{
			return View();
		}
		public FileResult DownloadFile()
		{
			string file = System.IO.Path.Combine(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(System.IO.Path.Combine(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 use System.IO.Compression to create zip archives:

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

namespace Website.Controllers
{
	public class ZipController : Controller
	{
		public ZipController() {}

		public IActionResult Index()
		{
			return View();
		}
		public async Task<FileResult> DownloadFile()
		{
			using (MemoryStream zipoutput = new MemoryStream())
			{
				using (ZipArchive archive = new ZipArchive(zipoutput, ZipArchiveMode.Create, false))
				{
					ZipArchiveEntry entry = archive.CreateEntry("test.txt", CompressionLevel.Optimal);
					using (var entryStream = entry.Open())
					{
						byte[] buffer = Encoding.UTF8.GetBytes("This is a test!!!");
						using (var ms = new MemoryStream(buffer))
						{
							await ms.CopyToAsync(entryStream);
						}
					}
					entry = archive.CreateEntry("test2.txt", CompressionLevel.Optimal);
					using (var entryStream = entry.Open())
					{
						byte[] buffer = Encoding.UTF8.GetBytes("This is another test!!!");
						using (var ms = new MemoryStream(buffer))
						{
							await ms.CopyToAsync(entryStream);
						}
					}
				}
				return File(zipoutput.ToArray(), "application/x-zip-compressed", "test-txt-files.zip");
			}
		}
	}
}

Or read simple 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");
			}
		}
	}
}

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE