Having trouble with ASP.NET MVC 4 image uploading - asp.net-mvc

I'm trying to upload a image along with other fields. Been following examples from here and here
However it seems that the image object isn't passed to the controller and i'm unable to find any error.
Here's the view:
#model Project.Models.ContentNode
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm("Create", "News", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>News</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
#*Image upload field*#
<div class="editor-field">
<input type="file" name="file" />
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Body)
</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.Body)
#Html.ValidationMessageFor(model => model.Body)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Here are the controller methods:
public ActionResult Create()
{
var model = new ContentNode();
return View( model );
}
[HttpPost]
public ActionResult Create(ContentNode nyF, HttpPostedFileBase imageData)
{
// Get the type of content from DB
var k = (from ct in _db.ContentTypes
where ct.ID == 1
select ct).Single();
var curUser = (from u in _db.Users
where u.Username == User.Identity.Name
select u).Single();
nyF.Author = curUser;
nyF.ContentType = k;
nyF.dateCreated = DateTime.Now;
_db.ContentNodes.Add(nyF);
_db.SaveChanges();
// Process image
if ((imageData != null && imageData.ContentLength > 0))
{
var fileName = Path.GetFileName(imageData.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
imageData.SaveAs(path);
}
else
{
return Content("The image object wasn't received");
}
return View(nyF);
}
I've read over the whole thing over and over and am unable to see the error. Anyone here willing to point out what I'm doing wrong?

The name of the input file needs to match the action parameter
<input type="file" name="file" />
should be
<input type="file" name="imageData" />
or you change your action parameter name
public ActionResult Create(ContentNode nyF, HttpPostedFileBase file)

You can upload file, using Request.Files in your post method.
You can validate uploaded file using following code.
Request.Files[0].ContentLength

Related

How to retain formcollection values on postback in MVC

Here I am using simple application and on clicking send button I will execute Index httppost action. But for some reason if my captcha is not correct then i am trying to load the same view. I am passing form collection so my firstname and lastname are not wiped out. Please suggest how can i persist my values.
[HttpPost]
public ActionResult Index(FormCollection dataColl)
{
ColComments datgrp = new ColComments();
datgrp.fname = dataColl[0].ToString();
datgrp.lname = dataColl[1].ToString();
if (!this.IsCaptchaValid(""))
{
ViewBag.Classname = "alert alert-warning";
ViewBag.ErrorMessage = "Incorrect captcha answer.";
}
else
{
ViewBag.ErrorMessage = "OKAY";
return RedirectToAction("Landing", "Account");
}
return View(dataColl);
}
Index view.
#using CaptchaMvc.HtmlHelpers
#model CaptchaTestApp.Models.ColComments
#{
ViewBag.Title = "Home Page";
}
<div class="jumbotron">
<h1>ASP.NET</h1>
<p class="lead">ASP.NET is a free web framework for building great Web sites and Web applications using HTML, CSS and JavaScript.</p>
<p>Learn more ยป</p>
</div>
<div>
#using (Html.BeginForm())
{
<div> #Html.ValidationSummary(true)</div>
<fieldset>
<legend>ColComments</legend>
<div class="editor-label">
#Html.LabelFor(model => model.fname)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.fname)
#Html.ValidationMessageFor(model => model.fname)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.lname)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.lname)
#Html.ValidationMessageFor(model => model.lname)
</div>
</fieldset>
<table>
<tr>
<td colspan="3">
#Html.Captcha(8, "_captchaCnt")
</td>
</tr>
</table>
<p>
<input type="submit" value="Send" />
</p>
}
</div>
You need to return the same view which you are sending in HttpGet method with updated values. You are sending form collection object to view.
[HttpPost]
public ActionResult Index(FormCollection dataColl)
{
ColComments datgrp = new ColComments();
datgrp.fname = dataColl[0].ToString();
datgrp.lname = dataColl[1].ToString();
if (!this.IsCaptchaValid(""))
{
ViewBag.Classname = "alert alert-warning";
ViewBag.ErrorMessage = "Incorrect captcha answer.";
}
else
{
ViewBag.ErrorMessage = "OKAY";
return RedirectToAction("Index", "Home");
}
return View(datgrp);
}

MVC file saving and uploading with same button

I have a Upload and Save forms that works but I would like to pass uploaded image and save other data for movies in one button (to merge Save and Upload)
Here is my cshtml Create view:
#model MvcMovie.Models.Movie
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ReleaseDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ReleaseDate)
#Html.ValidationMessageFor(model => model.ReleaseDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Genre)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Genre)
#Html.ValidationMessageFor(model => model.Genre)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Price)
#Html.ValidationMessageFor(model => model.Price)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Rating)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Rating)
#Html.ValidationMessageFor(model => model.Rating)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ImageUrl)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ImageUrl)
#Html.ValidationMessageFor(model => model.ImageUrl)
</div>
<input type="submit" value="Create" />
</fieldset>
<img src="~/Content/Images/Full/image1.JPG" alt="Sample Image" width="300px" height="200px" />
}
#using (Html.BeginForm("Upload", "Movies", FormMethod.Post, new { enctype="multipart/form-data" }))
{
<input name="ImageUploaded" type="file">
<input type="submit" value="Upload"/>
}
#Html.ActionLink("Back to List", "Index")
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Here's my Controller:
public ActionResult Create()
{
return View();
}
//
// POST: /Movies/Create
[HttpPost]
public ActionResult Create(Movie movie)
{
if (ModelState.IsValid)
{
db.Movies.Add(movie);
db.SaveChanges();
return RedirectToAction("Index", "Movies");
}
return View(movie);
}
public ActionResult Edit(int id = 0)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
[HttpPost]
public ActionResult Upload(ImageModel model)
{
if (ModelState.IsValid)
{
string fileName = model.ImageUploaded.FileName;
string serverPath = Server.MapPath("~");
string imagesPath = serverPath + "Content\\Images\\";
String fullPath = #"" + "C:/Users/FAZI-7/Desktop/Full/";
String thumbPath = #"" + "C:/Users/FAZI-7/Desktop/Thumb/";
ImageModel.ResizeAndSave(thumbPath, fileName, model.ImageUploaded.InputStream, 80, false);
ImageModel.ResizeAndSave(fullPath, fileName, model.ImageUploaded.InputStream, 600, false);
}
return RedirectToAction("Index", "Movies");
}
I can't make one button for both functions, I have read that I might need to have the same controller action having two parameters, or put all the controls in one form, but how would I do this?
Here's a working example. I think one of the critical pieces you're missing is the enctype parameter on your HTML form element.
Create.shtml
#model ARES.Models.EmployeeViewModel
#{
ViewBag.Title = "Create New Employee";
}
#using (Html.BeginForm("Create", "Employees", FormMethod.Post, new { enctype = "multipart/form-data" })) {
<fieldset class="containedAndCentered">
<div class="editor-label">
Department
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.SelectedDepartmentId, new SelectList(Model.Departments, "department_id", "department_name", Model.SelectedDepartmentId))
#Html.ValidationMessageFor(model => model.Departments)
</div>
<div class="editor-label">
Name
</div>
<div class="editor-field">
#Html.EditorFor(model => model.EmployeeName)
#Html.ValidationMessageFor(model => model.EmployeeName)
</div>
<div class="editor-label">
Photo
</div>
<div class="editor-field">
<input type="file" id="EmployeePhoto" name="EmployeePhoto" />
</div>
<div class="editor-field">
#Html.HiddenFor(model => model.EmployeeId)
</div>
<hr />
<div class="controls left">
#Html.ActionLink("Back to List", "Index")
</div>
<div class="controls right">
<input type="submit" value="Create" />
</div>
</fieldset>
}
EmployeesController.cs
namespace ARES.Controllers {
public class EmployeesController : Controller {
private employee_picsEntities db = new employee_picsEntities();
[HttpPost]
public ActionResult Create(EmployeeViewModel evm) {
if (ModelState.IsValid) {
// Find the selected department object.
var department = (from d in db.departments
where d.department_id == evm.SelectedDepartmentId
select d).FirstOrDefault();
employee_master employee_master = new employee_master() {
department = department,
employee_name = evm.EmployeeName,
file_location = evm.EmployeePictureUrl,
status = 1
};
// Add the employee to the collection before we set the file_location field so that we have the neccessary employee_id value.
db.employee_master.Add(employee_master);
db.SaveChanges();
// Save a photo if we were provided with one.
if (evm.EmployeePhoto != null) {
employee_master.file_location = SaveEmployeePhoto(employee_master.employee_id, evm.EmployeePhoto);
}
//db.employee_master.Add(employee_master);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(evm);
}
public string SaveEmployeePhoto(int employeeId, HttpPostedFileBase employeePhoto) {
var basePath = "~/Content/images/photos";
var filename = "";
var path = Server.MapPath(basePath);
var savedFileName = "";
HttpPostedFileBase file = Request.Files[0];
if (file.ContentLength > 0) {
filename = employeeId + Path.GetExtension(file.FileName);
savedFileName = Path.Combine(path, filename);
file.SaveAs(savedFileName);
}
//filename = (basePath + "/" + fileName).ToAbsoluteUrl()
return string.Format("{0}/{1}", basePath, filename).ToAbsoluteUrl();
}
}
}

MVC4 Pass custom view Model back to Controller Action

I have a custom class
public class BloggerViewModel
{
public Person Blogger;
public List<BloggerWebsite> BloggerWebsites;
}
That I pass into a view
[HttpGet]
public ActionResult Edit(int id)
{
blogger = GetById(id);
var WebSites = GetBloggersWebsites(Id);
var BloggerViewModel = new BloggerViewModel();
BloggerViewModel.Blogger = blogger;
BloggerViewModel.BloggerWebsites = WebSites;
return View(BloggerViewModel);
}
Then when I post back to the edit action
[HttpPost]
public ActionResult Edit(BloggerViewModel entity)
{
return View(entity);
}
entity is null.
My view is something like this ( I took out allot of the code that you didn't need to see. Such as most of the text box bindings)
#model Bloginect.Model.Models.BloggerViewModel
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Person</legend>
#Html.HiddenFor(model => model.Blogger.Id)
<div class="editor-label">
#Html.LabelFor(model => model.Blogger.FirstName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Blogger.FirstName)
#Html.ValidationMessageFor(model => model.Blogger.FirstName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Blogger.LastName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Blogger.LastName)
#Html.ValidationMessageFor(model => model.Blogger.LastName)
</div>
<div class="editor-field">
#if (Model.BloggerWebsites[0].Website != null)
{
Html.EditorFor(model => model.BloggerWebsites[0].Website);
}
else
{
#Html.TextBox("Website1")
}
</div>
<div class="editor-field">
#if (Model.BloggerWebsites[1].Website != null)
{
Html.EditorFor(model => model.BloggerWebsites[0].Website);
}
else
{
#Html.TextBox("Website2")
}
</div>
<div class="editor-field">
#if (Model.BloggerWebsites[2].Website != null)
{
Html.EditorFor(model => model.BloggerWebsites[0].Website);
}
else
{
#Html.TextBox("Website3")
}
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Is there something I am doing wrong? I have checked out some of the other replies to similar questions on this and they did not answer my question.
You must use editors to handle the bindings to your nested model properties, a good example on how to do it can be found here.

Hidden ViewModel Property defaulting to 0 when null - should stay null

Get Controller Method:
public ActionResult Create(int? parentId)
{
var model = new CreatePersonViewModel();
// pull set from db
var parent = _db.Persons.FirstOrDefault(s => s.PersonId == parentId);
if (parent != null)
{
model.ParentId = parent.PersonId;
}
return View("Create", model);
}
POST:
public ActionResult Create(CreatePersonViewModel viewModel) // viewModel.ParentId is 0 when it's passed to this method from the view, even though I didn't set it, and it's null coming from GET.
{
//bla bla
}
My View:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>CreatePersonViewModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
#Html.HiddenFor(model => model.ParentId) // tried with second parameter of "null" as well
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Any idea why the view is sending back a value of 0 in that property for cases when it should be null? I don't have any details to add here but the form validation is asking for more text so here it is.

ASP.NET MVC File Upload Error - "The input is not a valid Base-64 string"

I'm trying to add a file upload control to my ASP.NET MVC 2 form but after I select a jpg and click Save, it gives the following error:
The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or a non-white space character among the padding characters.
Here's the view:
<% using (Html.BeginForm("Save", "Developers", FormMethod.Post, new {enctype = "multipart/form-data"})) { %>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
Login Name
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.LoginName) %>
<%: Html.ValidationMessageFor(model => model.LoginName) %>
</div>
<div class="editor-label">
Password
</div>
<div class="editor-field">
<%: Html.Password("Password") %>
<%: Html.ValidationMessageFor(model => model.Password) %>
</div>
<div class="editor-label">
First Name
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.FirstName) %>
<%: Html.ValidationMessageFor(model => model.FirstName) %>
</div>
<div class="editor-label">
Last Name
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.LastName) %>
<%: Html.ValidationMessageFor(model => model.LastName) %>
</div>
<div class="editor-label">
Photo
</div>
<div class="editor-field">
<input id="Photo" name="Photo" type="file" />
</div>
<p>
<%: Html.Hidden("DeveloperID") %>
<%: Html.Hidden("CreateDate") %>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
And the controller:
//POST: /Secure/Developers/Save/
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Developer developer)
{
//get profile photo.
var upload = Request.Files["Photo"];
if (upload.ContentLength > 0)
{
string savedFileName = Path.Combine(
ConfigurationManager.AppSettings["FileUploadDirectory"],
"Developer_" + developer.FirstName + "_" + developer.LastName + ".jpg");
upload.SaveAs(savedFileName);
}
developer.UpdateDate = DateTime.Now;
if (developer.DeveloperID == 0)
{//inserting new developer.
DataContext.DeveloperData.Insert(developer);
}
else
{//attaching existing developer.
DataContext.DeveloperData.Attach(developer);
}
//save changes.
DataContext.SaveChanges();
//redirect to developer list.
return RedirectToAction("Index");
}
Thanks,
Justin
I have recently found a solution for this, although I am now using MVC3 rather than MVC2.
In the Save action, exclude the binary field from the bound object and include a separate HttpPostedFileBase field:
e.g.
public ActionResult Save([Bind(Exclude = "Photo")]Developer developer, HttpPostedFileBase Photo) {...}
Note that you can also do this to avoid having to include the Html.Hidden elements from your View. e.g.:
public ActionResult Save([Bind(Exclude = "Photo,DeveloperID,CreateDate")]Developer developer, HttpPostedFileBase Photo) {...}
You can then use this HttpPostedFileBase object directly rather than needing to access Request.Files.
Personally, I actually store these types of images in the database in an SQL "image" field using this code:
if (Picture != null)
{
if (Picture.ContentLength > 0)
{
byte[] imgBinaryData = new byte[Picture.ContentLength];
int readresult = Picture.InputStream.Read(imgBinaryData, 0, Picture.ContentLength);
Developer.Picture = imgBinaryData;
}
}
Hope this is helpful...
Mark
I just tried your code and was able to upload without any issues. I did not save to the database nor does my Developer class have a Photo property.
namespace MvcApplication5.Controllers
{
public class Developer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime UpdateDate { get; set; }
public int DeveloperID { get; set; }
public string LoginName { get; set; }
public string Password { get; set; }
}
}
Controller
public class DefaultController : Controller
{
//
// GET: /Default/
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Developer developer)
{
//get profile photo.
var upload = Request.Files["Photo"];
if (upload.ContentLength > 0)
{
string savedFileName = Path.Combine(
#"C:\temp",
"Developer_" + developer.FirstName + "_" + developer.LastName + ".jpg");
upload.SaveAs(savedFileName);
}
developer.UpdateDate = DateTime.Now;
if (developer.DeveloperID == 0)
{//inserting new developer.
}
else
{//attaching existing developer.
}
//save changes.
//redirect to developer list.
return RedirectToAction("Index");
}
}
View
<div>
<% using (Html.BeginForm("Save", "Default", FormMethod.Post, new { enctype = "multipart/form-data" }))
{ %>
<%: Html.ValidationSummary(true)%>
<fieldset>
<legend>Fields</legend>
<div class="editor-label">
Login Name
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.LoginName)%>
<%: Html.ValidationMessageFor(model => model.LoginName)%>
</div>
<div class="editor-label">
Password
</div>
<div class="editor-field">
<%: Html.Password("Password")%>
<%: Html.ValidationMessageFor(model => model.Password)%>
</div>
<div class="editor-label">
First Name
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.FirstName)%>
<%: Html.ValidationMessageFor(model => model.FirstName)%>
</div>
<div class="editor-label">
Last Name
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.LastName)%>
<%: Html.ValidationMessageFor(model => model.LastName)%>
</div>
<div class="editor-label">
Photo
</div>
<div class="editor-field">
<input id="Photo" name="Photo" type="file" />
</div>
<p>
<%: Html.Hidden("DeveloperID")%>
<%: Html.Hidden("CreateDate")%>
<input type="submit" value="Save" />
</p>
</fieldset>
<%} %>
</div>
I got the same issue. here is the solution I found.
Class property:
public byte[] Logo { get; set; }
View Code:
#using (Html.BeginForm("StoreMyCompany", "MyCompany", FormMethod.Post, new { id = "formMyCompany", enctype = "multipart/form-data" }))
{
<div class="form-group">
#Html.LabelFor(model => model.modelMyCompany.Logo, htmlAttributes: new { #class = "control-label col-md-3" })
<div class="col-md-6">
<input type="file" name="Logo" id="fileUpload" accept=".png,.jpg,.jpeg,.gif,.tif" />
</div>
</div>
}
Controller Code:
public ActionResult StoreMyCompany([Bind(Exclude = "Logo")]MyCompanyVM model)
{
try
{
Company objCompany = new Company();
byte[] imageData = null;
if (Request.Files.Count > 0)
{
HttpPostedFileBase objFiles = Request.Files["Logo"];
using (var binaryReader = new BinaryReader(objFiles.InputStream))
{
imageData = binaryReader.ReadBytes(objFiles.ContentLength);
}
}
}
catch (Exception ex)
{
Utility.LogError(ex);
}
return View();
}
}
i just excluded Logo from controller's call.
I have the same error, but the solution above didn't work for me, instead I notice that my Model property name is the same as the parameter name I am passing with the controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditSOP(File file, HttpPostedFileBase Upload)
My Model property name is
public byte[] Upload { get; set; }
After renaming it on a different parameter name, it now works:
public ActionResult EditSOP(File file, HttpPostedFileBase UploadFile)

Resources