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.
Thanks a lot man, you solved my issue
mate you just made an unproductive day in a productive day thanks
Glad to hear that! If you don’t mind, like us on FB/TW if you found our post useful :)
Thanks so much, I have tried this in Ajax.BeginForm but it doesn’t work, the same code in Html.BiginForm works well, does ajax not allow to upload file or should I adjust my code somewhere? And sorry for my bad English if anything not clear just tell me, thx!
Hello there,
I’m happy you did like our post: if you found it useful, don’t forget to add a like on our Facebook page :)
Regarding your query, I personally do not like Ajax.BeginForm for a number of reasons: whenever I need to post a form using AJAX, I always end up using a JQuery form plugin or other JS-based tools that allow me to manually setup and fine-tune the ajax request/response cycle.
Here’s a good example from StackOverflow, including full source code:
https://stackoverflow.com/a/19044689/1233379