Upload image included in MVC model - asp.net-mvc

I have the following model:
public class Photo
{
public int PhotoId { get; set; }
public byte[] ImageData { get; set; }
public DateTime DateUploaded { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
}
I would like the user to be able to enter the details for the photo then post the model the the controller. My controller action is as follows:
[HttpPost]
public ActionResult Create(WilhanWebsite.DomainClasses.Photo photo)
{
if (ModelState.IsValid)
{
photo.DateUploaded = DateTime.Now;
_context.Photos.Add(photo);
_context.SaveChanges();
return RedirectToAction("Index");
}
//we only get here if there was a problem
return View(photo);
}
My view is as follows:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Photo</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.ImageData, new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" name="uploadImages" class="input-files" />
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DateUploaded, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateUploaded)
#Html.ValidationMessageFor(model => model.DateUploaded)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Description, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IsActive, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.IsActive)
#Html.ValidationMessageFor(model => model.IsActive)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
The view is displayed ok and allows the user to select a file from their local disk and enter the other model details.
My problem is that although the model is posted to the controller ok, the Description, Date and IsActive flags are populated ok - the Image data is null.
Could anyone please let me know what I need to change so that the byte array for the photo is included in the model posted to the controller?

The file input in your view has a name uploadImages. I can't see a property with this name in your view model. You seem to have some ImageData property which is a byte array, but there doesn't seem to be a corresponding input field with this name in your view.
This explains why you get null. You could make this work by respecting the convention. So for example if you intend to have such an input field in your view:
<input type="file" name="uploadImages" class="input-files" />
then make sure that you have a property on your view model with the same name. And of course of type HttpPostedFileBase.
public HttpPostedFileBase UploadImages { get; set; }
Also in your view make sure you are setting the proper content type of multipart/form-data:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}
You probably might want to go through the following blog post to better familiarize yourself with the basics of how uploading of files work in ASP.NET MVC. I've also written a similar answer here that you might consult.
So once you add the HttpPostedFileBase property with the UploadImages name in your view model you could adapt your controller action to read the byte array and store it your ImageData property:
[HttpPost]
public ActionResult Create(WilhanWebsite.DomainClasses.Photo photo)
{
if (ModelState.IsValid)
{
photo.DateUploaded = DateTime.Now;
photo.ImageData = new byte[photo.UploadImages.ContentLength];
photo.UploadImages.Read(photo.ImageData, 0, photo.ImageData.Length);
_context.Photos.Add(photo);
_context.SaveChanges();
return RedirectToAction("Index");
}
//we only get here if there was a problem
return View(photo);
}
Now bear in mind that this is an absolutely awful solution. Never do that in a real world application. In a correctly designed application you will have a view model that your controller action will take as a parameter. You're never gonna directly use your autogenerated EF model as parameter to your controller action. You will have a view model with the HttpPostedFileBase property which will be mapped to your domain model.
So in a properly designed application you will have a PhotoViewModel view model class that your controller action will take.

Change this line:
#using (Html.BeginForm())
To this:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
Then change:
<input type="file" name="uploadImages" class="input-files" />
To:
<input type="file" name="ImageData" class="input-files" />
Then change this line:
public byte[] ImageData { get; set; }
To this:
public HttpPostedFileBase ImageData { get; set; }
Finally, use some code like this to read the image into a byte array:
var bs = new byte[ImageData.ContentLength];
using (var fs = ImageData.InputStream)
{
var offset = 0;
do
{
offset += fs.Read(bs, offset, bs.Length - offset);
} while (offset < bs.Length);
}

View:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
<input type="file" id="ImageFile" name="ImageFile" .../>
...
}
Controller:
[HttpPost]
public ActionResult Create(Photo photo, HttpPostedFileBase ImageFile)
{
byte[] buf = new byte[ImageFile.ContentLength];
ImageFile.InputStream.Read(buf, 0, buf.Length);
photo.ImageData = buf;
...
}

Related

MVC Is there a way to make sure a specific ViewModel property doesn't get passed in a Html.BeginForm(action, controller, ModelData)?

ANSWERED
See answers section below
I've been struggling for hours with this problem and I haven't found anything that relates, so apologies if this post is a duplicate.
I'll start by constructing a problem related to my own.
Let's say we create a ViewModel:
public class XmlViewModel(){
[Required]
public string Code { get; set; }
public string XML { get; set; }
}
and a constructor method which loads the View and as well as another that gets called when the submit is registered on the View.
public class Extractor{
public ActionResult Index()
{
XmlViewModel xmlVM = new XmlViewModel ()
{
XML = "Sample XML";
};
return View(xmlVM);
}
public ActionResult GetXml(XmlViewModel xmlVM){
xmlVM.XML = GetXMLByCode();
return View ("Index", xmlVM)
}
}
Then the view Index as below
#model Project.ViewModel.XmlViewModel
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm("GetXml", "Extractor", Model))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Code, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Code, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Code, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.XML, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-9">
<pre id="XML">#Html.Raw(Html.Encode(Model.XML))</pre>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Generate XML" class="btn btn-default"/>
</div>
</div>
</div>
}
So in this scenario I'm starting the page with:
When the user clicks Submit using a XML Code, I want the Code to be run against some collection of XML, then that returned XML replaces the "SampleXML"
Problem is when the form gets submitted again(Twice) (Now with the XML field holding a few hundred characters) it overloads the Query and returns this:
Because yep you guessed it, the XML fills up the Request with the XML from the previous Form result.
So my question is, is there any way to clear the ViewModel Property so it isn't passed in the Query, or some attribute to add that will tell the ViewModel to not pass the property through the Html.BeginForm()?
If possible I would like to stay away from passing the ViewModel properties individually as the actual problem's ViewModel is more complicated and it would be troublesome going down that route.
After my suggestions, if you are still getting header to large, I can help diagnose.
Add an xml file to your project. Add tons of xml to it. Right click to get properties. For build action, change to embedded resource.
Where I have WebApplication2.XMLFile1.xml, you should have your assembly name dot then your file name. You can right click on your project, and see properties to get assembly name.
Here is my code
namespace WebApplication2.Controllers
{
public class XmlViewModel
{
[Required]
public string Code { get; set; }
public string XML { get; set; }
}
public class HomeController : Controller
{
public static string GetXMLByCode()
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "WebApplication2.XMLFile1.xml";
string result = String.Empty;
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
}
return result;
}
public ActionResult GetXml(XmlViewModel xmlVM)
{
xmlVM.XML = GetXMLByCode();
return View("Index9", xmlVM);
}
public ActionResult Index9()
{
XmlViewModel xmlVM = new XmlViewModel { XML = "Sample XML" };
return View(xmlVM);
}
here is my view
#model WebApplication2.Controllers.XmlViewModel
#{
ViewBag.Title = "Index9";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("GetXml", "Home", Model))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Code, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Code, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Code, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.XML, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-9">
<pre id="XML">#Html.Raw(Html.Encode(Model.XML))</pre>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Generate XML" class="btn btn-default" />
</div>
</div>
</div>
}
ANSWERED
I eventually figured it out by doing the following in the view:
#{
ViewBag.Title = "Index";
ViewBag.XML= XmlViewModel.XML;
XmlViewModel.XML = "";
}
---
<div class="form-group">
#Html.LabelFor(model => model.XML, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-9">
<pre id="XML">#Html.Raw(Html.Encode(ViewBag.XML))</pre>
</div>
</div>
Now when the view is loaded, the Viewbag will hold the XML being returned and I can safely empty the model data before returning it to the Html.BeginForm()
Keeping for anyone in the same situation

Troubleshooting simple create-from

I am trying to read a value from a view in aps.net mvc: I am aware that this seems like a very basic issue, however, i could not find any solution for this, so i am turning to you: In my case, it seems as if the parameter playlistModel.Model.Name is never sent, or at least is null.
My controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(PlaylistViewModelDetails playlistModel)
{
if (!String.IsNullOrEmpty(playlistModel.Model.Name))
{
//this is never called due to playlistModel.Model.Name being null.
return RedirectToAction("Index");
}
return View(playlistModel);
}
#model Orpheus.Models.ViewModels.PlaylistViewModelDetails
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Erstellen" class="btn btn-default" />
</div>
</div>
</div>
}
public class PlaylistViewModelDetails
{
public PlaylistModel Model = new PlaylistModel(); //a seperate class containing a string value, which must be read from the form
}
Thank you for helping me to solve this issue!
Your PlaylistViewModelDetails contains only a field for Model. The DefaultModelBinder only binds properties, not fields.
Change your model to
public class PlaylistViewModelDetails
{
public PlaylistModel Model { get; set; }
}
and add a parameter-less constructor if you want to initialize PlaylistModel
public PlaylistViewModelDetails()
{
Model = new PlaylistModel();
}
Note also Name in PlaylistModel also need to be a property.

Store View form in existing database

Forgive the newbie ASP.NET MVC question. I am used to tutorials where Code First is used with the Entity Framework. Here, this is not the case. I have a form that I want the user to fill out. When it has been filled out, I want to use EF to write the values to an existing database. I can't figure out how to "trap" the values in the view so I can write my EF code. I used a model and I redirected the BeginForm to an "Edit" action method but I don't know how to get my filled in class. Here is the HomeController methods:
[HttpGet]
public ActionResult Trial()
{
UserAccount account = new UserAccount();
return View(account);
}
public ActionResult Edit()
{
}
Here is the model class:
public class UserAccount
{
public int AccountID { get; set; }
public string AccountName { get; set; }
public string RegistrationCode { get; set; }
public DateTime Created { get; set; }
}
}
Here is the View the wizard generated. When I hit the "Create" button, I want to go to the "Edit" action menu or someplace I can use EF to write to the existing database table. How do I do this?
#model AlphaFrontEndService.Models.UserAccount
#{
ViewBag.Title = "Trial";
}
<h2>Trial</h2>
#using (Html.BeginForm("Edit", "Home"))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>UserAccount</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.AccountID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AccountID)
#Html.ValidationMessageFor(model => model.AccountID)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AccountName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.AccountName)
#Html.ValidationMessageFor(model => model.AccountName)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RegistrationCode, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.RegistrationCode)
#Html.ValidationMessageFor(model => model.RegistrationCode)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Created, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Created)
#Html.ValidationMessageFor(model => model.Created)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
You need a POST action for Trial method like below:
[HttpPost]
public ActionResult Trial(UserAccount model)
{
if (ModelState.IsValid)
{
//Store the form data into your database
}
return View(model);
}
Then in your view, add a submit button element inside the form, also instead of Edit, you just need to use Trial for the postback.
#using (Html.BeginForm("Trial", "Home")) {
//
<input type="submit" value="Submit"/>
}
Note: You don't need to create other Edit action method if you don't have some other reasons.
If you don't know how to save the data to your database, below is an example:
Create your DbContext class
public class MyDbContext : DbContext
{
public MyDbContext()
: base("name=YourDbConnection")
{
}
public DbSet<UserAccount> UserAccounts { get; set; }
}
Then the action method will looks like:
public class HomeController : Controller
{
//
[HttpPost]
public ActionResult Trial(UserAccount model)
{
if (ModelState.IsValid)
{
using (var db = new MyDbContext())
{
db.UserAccounts.Add(model);
db.SaveChanges();
return RedirectToAction("Index");
}
}
return View(model);
}
}

ASP.NET MVC5 - Image upload to SQL Server 2008R2 Standard

I am semi-new to ASP.NET MVC, I have used it in the past but not to the extent of what I have been planning to use it for and to learn from for some projects I am working on. I know this question has been asked a lot over the internet and there are many solutions so I will try and keep this as specific as possible regarding uploading images and storing them in a SQL Server database.
I am currently using .NET 4.5 with MVC5 (VS 2013) to do all my coding.
To begin I found a great tutorial that got me up and running with being able to upload images to my SQL Server 2008 database: http://www.mikesdotnetting.com/Article/125/ASP.NET-MVC-Uploading-and-Downloading-Files
Everything works great after I figured out how the functions worked from the site, but the issue I am having is that I have multiple text fields that I want to include data from into the SQL Server database along with including an image upload feature that I created based off the tutorial I found online.
The first part of my code is the Models (BeerList.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace YBPP_Production.Models
{
public class BeerList
{
public int ID { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
[Required(ErrorMessage = "Company is required.")]
public string Company { get; set; }
[Required(ErrorMessage = "Type is required.")]
public string Type { get; set; }
[Required(ErrorMessage = "City is required.")]
public string City { get; set; }
[Required(ErrorMessage = "State is required.")]
public string State { get; set; }
[Required(ErrorMessage = "Country is required")]
public string Country { get; set; }
public string ABV { get; set; }
public string IBU { get; set; }
}
public class Info : DbContext
{
public DbSet<DBName> MoreDB { get; set; }
}
}
The string listed there are the text fields I am trying to pull into my database and I can do that but when I try to mix in the image upload feature either the text will upload or the image will upload depending on how I do the calls.
The Controllers section of my code (BeerListController.cs)
// GET: /BeerList/Create
public ActionResult Create()
{
return View();
}
// POST: /BeerList/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Name,Company,Type,City,State,Country,ABV,IBU")] BeerList beerlist)
{
if (ModelState.IsValid)
{
db.BeerListDB.Add(beerlist);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(beerlist);
}
public ActionResult FileUpload(HttpPostedFileBase file)
{
//Begin the Image Uploading Process
foreach (string upload in Request.Files)
{
if (!Request.Files[upload].HasFile()) continue;
string mimeType = Request.Files[upload].ContentType;
Stream fileStream = Request.Files[upload].InputStream;
string fileName = Path.GetFileName(Request.Files[upload].FileName);
int fileLength = Request.Files[upload].ContentLength;
byte[] fileData = new byte[fileLength];
fileStream.Read(fileData, 0, fileLength);
const string connect = #"Server=localhost;database=<database-name>;uid=<username-here>;pwd=<there-a-passwordhere>";
using (var conn = new SqlConnection(connect))
{
var qry = "INSERT INTO BeerLists (FileContent, mimeType, FileName) VALUES (#FileContent, #mimeType, #FileName)";
var cmd = new SqlCommand(qry, conn);
cmd.Parameters.AddWithValue("#FileContent", fileData);
cmd.Parameters.AddWithValue("#MimeType", mimeType);
cmd.Parameters.AddWithValue("#FileName", fileName);
conn.Open();
cmd.ExecuteNonQuery();
}
}
return View();
}
The view part of my code (Create.cshtml)
#model YBPP_Production.Models.BeerList
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype="multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>BeerList</h4>
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Company, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Company)
#Html.ValidationMessageFor(model => model.Company)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Type, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Type)
#Html.ValidationMessageFor(model => model.Type)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.City, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.State, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.State)
#Html.ValidationMessageFor(model => model.State)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Country, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Country)
#Html.ValidationMessageFor(model => model.Country)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ABV, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ABV)
#Html.ValidationMessageFor(model => model.ABV)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.IBU, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.IBU)
#Html.ValidationMessageFor(model => model.IBU)
</div>
</div>
<div class="form-group">
<p class="control-label col-md-2">Image Upload:</p>
<div class="col-md-10">
<input type="file" id="ImageUpload" name="ImageUpload" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" name="Submit" id="Submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
So what I am looking to hopefully get help with is being able to merge the function of Create and FileUpload into one function so that the form will take both the text in the text-fields and the image that gets uploaded with it. I have read a bunch of other people's posts and code and they all seem to have their own way to doing things but everyone example never includes text-fields or any other form functions just the basic image upload.
Thank you very very much in advance for any direction/suggestions/help to this issue.
You can create model that contains HttpPostedFileBase, then save your entire form with selected file.
Model:
public class BeerListModel
{
public int ID { get; set; }
[Required(ErrorMessage = "Name is required")]
public string Name { get; set; }
[Required]
public HttpPostedFileBase file { get; set; }
}
View:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true)
<div class="form-group">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="form-group">
<p class="control-label col-md-2">Image Upload:</p>
<input type="file" id="file" name="file" />
</div>
<div class="form-group">
<input type="submit" name="Submit" id="Submit" value="Create" class="btn btn-default" />
</div>
</div>
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(BeerListModel model)
{
if (ModelState.IsValid)
{
//your logic
}
return View("Create");
}
#Szakus' answer is the overall direction you want to go with respect to uploading a file, however when you asked, " I am still a bit confused as to what I should put as far as my logic for the Controller" you inadvertently got to the root of your problem.
I would strongly recommend you do some research into separation of concerns. Putting raw SQL code in your controller is not a great idea and will open your application to a host of the other issues.
I know this doesn't really answer your question but having been where you are now I can tell you that the old adage, "an ounce of prevention is worth a pound of cure" will save you major headaches down the road.
If you want to see an example of a really good .net MVC project that you can use as sort of a template in building your skills I would recommend the open source shopping cart nopCommerce.

HttpPostedFileBase not binding to model

here is my ViewModel
public class FaultTypeViewModel
{
[HiddenInput(DisplayValue = false)]
public int TypeID { get; set; }
[Required(ErrorMessageResourceType = typeof(AdministrationStrings), ErrorMessageResourceName = "FaultTypeNameRequired")]
[Display(ResourceType = typeof(AdministrationStrings), Name = "FaultTypeName")]
public string TypeName { get; set; }
[Display(ResourceType = typeof(AdministrationStrings), Name = "FaultTypeDescription")]
[DataType(DataType.MultilineText)]
public string TypeDescription { get; set; }
[Display(ResourceType = typeof(AdministrationStrings), Name = "FaultTypeImageFile")]
public HttpPostedFileBase TypeImageFile { get; set; }
[HiddenInput(DisplayValue = false)]
public string TypeImageURL { get; set; }
}
Notice I have a "TypeImageFile" HttpPostedFileBase
I would expect that the model binder would bond that property from the form to the model passes to the controller bu I just keep receiving null.
here is the relevant code in the View:
#using (Html.BeginForm("AddFaultType","Administration", FormMethod.Post))
{
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×</button>
<h3 id="myModalLabel">#SharedStrings.Add #SharedStrings.FaultType</h3>
</div>
<div class="modal-body">
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.TypeName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TypeName)
#Html.ValidationMessageFor(model => model.TypeName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TypeDescription)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.TypeDescription)
#Html.ValidationMessageFor(model => model.TypeDescription)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.TypeImageFile)
</div>
<div class="editor-field">
<input type="file" name="TypeImageFile" id="TypeImageFile" />
</div>
</div>
<div class="modal-footer">
<input type="submit" value="#SharedStrings.Add" class="btn btn-primary" />
#Html.ActionLink(SharedStrings.Cancel, "Index", "Administration", null, new { Class = "btn", data_dismiss = "modal", aria_hidden = "true" })
</div>
}
and here is the controller:
[HttpPost]
public ActionResult AddFaultType(FaultTypeViewModel i_FaultToAdd)
{
var fileName = Path.GetFileName(i_FaultToAdd.TypeImageFile.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
i_FaultToAdd.TypeImageFile.SaveAs(path);
return RedirectToAction("Index");
}
Make sure you've set the enctype attribute on your form to multipart/form-data on your form if you want to be able to upload files:
#using (Html.BeginForm("AddFaultType", "Administration", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}
Completing Darin's answer:
Make sure you've set the enctype attribute on your form to multipart/form-data on your form if you want to be able to upload files:
#using (Html.BeginForm("AddFaultType", "Administration", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
...
}
To ensure your <input> is transmitted to the controller as part of the model use the Html Helpers for Id and name like below:
<input type="file" id="#Html.IdFor(x=>x.HttpPostedFileBase)" name="#Html.NameFor(x=>x.HttpPostedFileBase)" accept=".csv,.txt"/>
Works in MVC5 sorry I cant find any reference to which helpers are available in MVC3

Resources