Dealing with file uploads in ASP.NET / ASP.NET Core MVC can be tricky, expecially if we want to stick to the ViewModel pattern and handle them just like standard properties in our POCO classes.
If it weren't the case, we could easily work around it and do something like this in the View:
1 2 3 4 5 6 7 8 9 10 11 12 |
@model SomeModel; <!-- some HTML code --> @using (Html.BeginForm("Submit", "Home", FormMethod.Post, new { enctype="multipart/form-data" })) { <!-- some @Html.TextBoxFor() and other razor form controls bound to the model --> <input type="file" name="someFile" /> <input type="submit" value="Upload" name="UploadButton" id="UploadButton" /> } |
And then retrieve the file in the Controller in the following way:
1 2 3 4 5 6 7 |
[HttpPost] public ActionResult Submit(SomeModel model, HttpPostedFileBase someFile) { // do something with someFile return View(model); } |
To put it short, the file is kept outside the model and treated in a separate way.
This is a common and practical way to handle file uploads: I stumble upon this a number of times, from friends code to some GitHub project. Although being perfectly fine, it's definitely not the proper way to deal with such scenario: what if we are supposed to receive multiple files? Wouldn't be much better to have them inside the ViewModel instead?
Luckily enough, the MVC framework allows us to deal with it in a much better and cleaner way. Here's how:
In the ViewModel:
1 2 3 4 5 6 7 8 |
public class SomeModel() { public SomeModel() { } public HttpPostedFileBase SomeFile{ get; set; } } |
In The View:
1 2 3 4 5 6 7 8 9 10 11 12 |
@model SomeModel <!-- some stuff --> @using (Html.BeginForm("Submit", "Home", FormMethod.Post, new { enctype="multipart/form-data" })) { <!-- some @Html.TextBoxFor() and other razor form controls bound to the model --> @Html.TextBoxFor(m => m.SomeFile, new { type = "file" }) <input type="submit" value="Upload" name="UploadButton" id="UploadButton" /> } |
In the Controller:
1 2 3 4 5 6 7 |
[HttpPost] public ActionResult Submit(SomeModel model) { // do something with model.SomeFile return View(); } |
In case we need to support multiple files, we are free to choose between the following:
- create multiple properties and implement them separately just like the one above;
- change the public HttpPostedFileBase SomeFile property to something like public List<HttpPostedFileBase> SomeFiles and then span multiple @Html.TextBoxFor(m => m.SomeFile, new { type = "file" }) controls.
As always, the choice is ours.