Problem
How to upload and download files in ASP.NET Core MVC.
Solution
In an empty project, update Startup
class to add services and middleware for MVC:
public void ConfigureServices(
IServiceCollection services)
{
services.AddSingleton<IFileProvider>(
new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")));
services.AddMvc();
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Add a controller and action methods to upload and download file:
[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return Content("file not selected");
var path = Path.Combine(
Directory.GetCurrentDirectory(), "wwwroot",
file.GetFilename());
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return RedirectToAction("Files");
}
public async Task<IActionResult> Download(string filename)
{
if (filename == null)
return Content("filename not present");
var path = Path.Combine(
Directory.GetCurrentDirectory(),
"wwwroot", filename);
var memory = new MemoryStream();
using (var stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, GetContentType(path), Path.GetFileName(path));
}
Add a razor page with HTML form to upload file:
<form asp-controller="Home" asp-action="UploadFile" method="post"
enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">Upload File</button>
</form>
Discussion
Uploading
ASP.NET Core MVC model binding provides IFormFile
interface
to upload one or more files. The HTML form must have encoding
type set to multipart/form-data
and an input
element with type
attribute set to file
.
You could also upload multiple files by receiving a list of IFormFile
in action method and setting input
element with multiple
attribute:
[HttpPost]
public async Task<IActionResult> UploadFiles(List<IFormFile> files)
<input type="file" name="files" multiple />
You could also have IFormFile
as a property on model received by action method:
public class FileInputModel
{
public IFormFile FileToUpload { get; set; }
}
[HttpPost]
public async Task<IActionResult> UploadFileViaModel(FileInputModel model)
Note: Name on input elements must match action parameter name (or model property name) for model binding to work. This is no different than model binding of simple and complex types.
Downloading
Action method needs to return FileResult
with either a stream
, byte[]
or virtual path of the file. You will also need to know the content-type
of the file being downloaded. Here is a sample (quick/dirty) utility method:
private string GetContentType(string path)
{
var types = GetMimeTypes();
var ext = Path.GetExtension(path).ToLowerInvariant();
return types[ext];
}
private Dictionary<string, string> GetMimeTypes()
{
return new Dictionary<string, string>
{
{".txt", "text/plain"},
{".pdf", "application/pdf"},
{".doc", "application/vnd.ms-word"},
{".docx", "application/vnd.ms-word"},
{".xls", "application/vnd.ms-excel"},
{".xlsx", "application/vnd.openxmlformats
officedocument.spreadsheetml.sheet"},
{".png", "image/png"},
{".jpg", "image/jpeg"},
{".jpeg", "image/jpeg"},
{".gif", "image/gif"},
{".csv", "text/csv"}
};
}