i'm trying to upload a file with ASP MVC4, i give you my situation:
I have a model class "Movie" with some atributes (doesn't matter)
I want to add some code to my controller and View part without touching the model, because we want to make the image difrent to the model.
So, here's an example of my view code, i'll bold the lines added by me:
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Movie</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Duration)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Duration)
#Html.ValidationMessageFor(model => model.Duration)
</div>
//ADDED BY ME:
<div class="editor-label">
<p>Select a file</p>
</div>
<div class="editor-field">
<input type="file" name="fileUpload" />
</div>//END OF MY CODE
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
So you can see that i have some code generated by ASP which allows me to add a new user to the db, and a "input" added by me to upload the image.
The problem is that when i try to recover that image from the controller, the "Request.Files" atributte is empty, so i can't recover any image, and of course i can't upload it, here's my controller code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Movie movie)
{
if (ModelState.IsValid)
{
db.MovieContext.Add(movie);
db.SaveChanges();
foreach (string file in Request.Files)
{
var postedFile = Request.Files[file];
postedFile.SaveAs(Server.MapPath("~/UploadedFiles") + pelicula.Id);
}
return RedirectToAction("Index");
}
return View(movie);
}
Don't know why the "Request.Files" is empty, so if anyone can help me it would be great, Thank you so much
Try the below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Movie movie, HttpPostedFile fileUpload)
{
if (ModelState.IsValid)
{
db.MovieContext.Add(movie);
db.SaveChanges();
var postedFile = fileUpload;
postedFile.SaveAs(Server.MapPath("~/UploadedFiles") + pelicula.Id);
return RedirectToAction("Index");
}
return View(movie);
}
Lets see if we can find out whats wrong. Try the following and show us the errors:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Movie movie, HttpPostedFile fileUpload)
{
if (ModelState.IsValid)
{
db.MovieContext.Add(movie);
db.SaveChanges();
var postedFile = fileUpload;
postedFile.SaveAs(Server.MapPath("~/UploadedFiles") + pelicula.Id);
return RedirectToAction("Index");
}
var content = "";
foreach (ModelState modelState in ViewData.ModelState.Values)
{
foreach (ModelError error in modelState.Errors)
{
content += error.ErrorMessage + ", " + error.Exception + "<br/>";
}
}
return Content(content);
//return View(movie);
}
Antevirus has the answer but I hat to dig into comments to find it out. In short the problem for was that I didnt specify any encodings for my form which is
enctype = "multipart/form-data"
Here is the full code:
#using (Html.BeginForm("Create", "Members", FormMethod.Post, new { #id = "member-form", #enctype = "multipart/form-data" }))
{
<div class="form-group">
<label class="control-label col-md-2">Picture</label>
<div class="col-md-10">
<input id="fileUpload" type="file" name="fileUpload" class="file-loading">
</div>
</div>
}
I am using file-input for bootstrap
Related
Goal:
If you retrieve a error based on the input, it should be displayed in ValidationSummary in relation to ajax without the webpage being refreshed.
Problem:
I have tried to make it but it doesn't work so well.
What part am I missing?
Thank you!
Info:
*I have found some webpages but they do not fit exactly to my purpose.
*I'm using ASP.net mvc
#model WebApplication1.Controllers.PersonnelModel
#{
ViewBag.Title = "Ajax";
}
<h2>Ajax</h2>
<h2>AjaxViewModel</h2>
#using (Html.BeginForm("HtmlViewModel", "Home", null))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>PersonnelModel</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MailAdress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MailAdress)
#Html.ValidationMessageFor(model => model.MailAdress)
</div>
</fieldset>
<p>
<input type="submit" value="Html Form Action" />
</p>
}
<br/>
<br />
<h2>AjaxViewModel</h2>
#using (Ajax.BeginForm("AjaxViewModel", "Home", new AjaxOptions { UpdateTargetId = "result" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>PersonnelModel</legend>
<div id="result"></div>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.MailAdress)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.MailAdress)
#Html.ValidationMessageFor(model => model.MailAdress)
</div>
</fieldset>
<p>
<input type="submit" value="Ajax Form Action" />
</p>
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-ajax-unobtrusive#3.2.4/jquery.unobtrusive-ajax.min.js"></script>
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace WebApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult HtmlViewModel(PersonnelModel Pmodel)
{
return Content("Hi " + Pmodel.UserName + ", Thanks for the details, a mail will be sent to " + Pmodel.MailAdress + " with all the login details.", "text/html");
}
[HttpPost]
public ActionResult AjaxViewModel(PersonnelModel Pmodel)
{
/*
ModelState.AddModelError("", "login is fail");
return View("Index", Pmodel);
*/
return Content("Hi " + Pmodel.UserName + ", Thanks for the details, a mail will be sent to " + Pmodel.MailAdress + " with all the login details.", "text/html");
}
}
public class PersonnelModel
{
[Required(ErrorMessage = "UserName Required.")]
public string UserName { get; set; }
[Required(ErrorMessage = "Email id Required.")]
public string MailAdress { get; set; }
}
}
EDIT - 03/11/2017 : There is an easy way to do this
Create a partial view for the form, Let's call it Form.cshtml and move the markup needed for the form to that. For your ajax form, set the data-ajax-mode attribute value to replace and data-ajax-update value to the id of the same form.
If you are using Ajax.BeginForm helper method, this is how you will do
#model PersonnelModel
#using (Ajax.BeginForm("Create", "Home",
new AjaxOptions { UpdateTargetId = "myform",
InsertionMode = InsertionMode.Replace },new { id="myform"}))
{
#Html.ValidationSummary("", true)
#Html.TextBoxFor(f => f.UserName)
#Html.ValidationMessageFor(model => model.UserName)
#Html.TextBoxFor(f => f.MailAdress)
#Html.ValidationMessageFor(model => model.MailAdress)
<input type="Submit" id="submit" value="Submit" class="btn btn-default" />
}
Now in your main view, simple call this partial view
#model PersonnelModel
<h2>My form below</h2>
#Html.Partial("Form",Model)
Now in the action method, when model state validation fails, return the partial view.
public ActionResult Create(PersonnelModel model)
{
if (ModelState.IsValid)
{
// to do : Save
}
if (Request.IsAjaxRequest())
{
return PartialView("Form",model);
}
return View(model);
}
Now when you submit the form and model state validation fails, the action method code will return the partial view result with the validation error messages (generated by the validation helpers) and the jquery.unobtrusive-ajax.js library code will replace (because we specified that with data-ajax-mode="replace") the content of the result of the jquery selector #data-ajax-update (the form tag and it's inner contents) with the response coming back from the server.
This should do it. Less client side code, compared to my old approach (below)
The Html.ValidationSummary method will be executed when the razor view gets executed. If you are doing a normal form post (non ajax), your action method usually returns to the same view when Model validation fails (assuming you write code like that) and the razor view code gets executed and the ValidationSummary method will read the validation errors from the model state dictionary and render the error messages.
When you use Ajax.BeginForm helper method the helper will generate some extra data attributes on the form and as long as you have included the jquery.unobtrusive-ajax.min.js script file, the form submit will be hijacked and it will do an ajax form submit instead.
When you do the ajax form submit, if you want to render model validation messages, you need to explicitly read the model validation errors and return that as a JSON response which your client side code can read and display in the UI.
[HttpPost]
public ActionResult Index(PersonnelModel model)
{
if (ModelState.IsValid)
{
return Json(new {status = "success", message= "Thanks for the details"});
}
else
{
var errors = new List<string>();
foreach (var modelStateVal in ViewData.ModelState.Values)
{
errors.AddRange(modelStateVal.Errors.Select(error => error.ErrorMessage));
}
return Json(new {status = "validationerror", errors = errors});
}
}
Now in your view, make sure you have a success handler for your ajax begin form to handle the response json
#using (Ajax.BeginForm("Index", "Home", new AjaxOptions { OnSuccess = "OnSuccess", }))
{
#Html.ValidationSummary("", true)
#Html.TextBoxFor(model => model.MailAdress)
<!--Your other form input elements-->
<input type="submit" value="Html Form Action" />
}
Note that i used #Html.ValidationSummary method with 2 overloads and passing an empty string as the first param. This will always render a ul element within the div which has class validation-summary-valid.
Now create your OnSuccess function and check the response and see whether response has a status property and the value of it is validationerror. If yes, loop through the errors collection and add a new li element with the error.
function OnSuccess(response) {
var $summaryUl = $(".validation-summary-valid").find("ul");
$summaryUl.empty();
if (response.status === 'validationerror') {
$.each(response.errors,
function(a, b) {
$summaryUl.append($("<li>").text(b));
});
} else {
alert(response.message);
}
}
I have the following code, you can enter the site from url: http://nirh1989-001-site1.itempurl.com/
When i open the url from desktop, i can upload multiple files at once, but when i open the url from my mobile browser i can only upload 1 file at a time.
Why?
Index.cshtml:
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="container">
<div class="form-horizontal">
<div class="form-group">
<p></p>
<label for="file">Upload Photo:</label>
<input type="file" name="file" id="file" accept="image/*" multiple="multiple" required />
</div>
<div class="form-group">
<div>
<input type="submit" value="Upload" class="btn btn-default" />
</div>
</div>
</div>
</div>
<hr />
<div class="gallery">
#if (ViewBag.Images != null)
{
var imgID = 0;
foreach (var image in (IEnumerable<string>)ViewBag.Images)
{
imgID++;
<a class="fancybox" rel="group" href="#Url.Content(image)">
<img id="#imgID" src="#Url.Content(image)" style="height: 100px; width: 100px;" />
</a>
}
}
</div>
}
Controller:
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
ViewBag.Images = Directory.EnumerateFiles(Server.MapPath("/Content/Photos")).Select(f => "/Content/Photos/" + Path.GetFileName(f));
return View();
}
//POST: Home
[HttpPost]
public ActionResult Index(IEnumerable<HttpPostedFileBase> file)
{
if (file != null)
{
foreach (var item in file)
{
string fileName = Path.GetFileName(item.FileName);
string imgPath = Server.MapPath("~/Content/Photos/");
item.SaveAs(imgPath + fileName);
}
}
ViewBag.Images = Directory.EnumerateFiles(Server.MapPath("/Content/Photos")).Select(f => "/Content/Photos/" + Path.GetFileName(f));
return View();
}
}
Apparently there was nothing wrong with my code... the problem was the mobile, i tried to upload multiple files from a folder in the phone that for some reason not allowed multiple choices... tried to upload from gallery and everything is ok
I have a MVC 5 controller with views, using Entity Framework project.
I have a controller and view for my Item Model:
public partial class Item
{
public int ItemID { get; set; }
public string ImageURL { get; set; }
}
I changed the Entity Framework Create view and controller to post an html file input and save the file path as the ImageURL
A snippet from the view looks like this:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">
<fieldset>
<legend>Upload a file</legend>
<div class="editor-field">
#Html.TextBox("file", "", new { type = "file" })
</div>
</fieldset>
</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>
}
The controller's Create ActionResult looks like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ItemID,ImageURL")] Item item, HttpPostedFileBase file)
{
try
{
if (ModelState.IsValid && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Uploads"), fileName);
file.SaveAs(path);
item.ImageURL = "/Uploads/" + file.FileName;
db.Items.Add(item);
db.SaveChanges();
}
ViewBag.Message = "Upload successful";
return RedirectToAction("Index");
}
catch
{
ViewBag.Message = "Upload failed";
ViewBag.ItemTypeID = new SelectList(db.ItemTypes, "ItemTypeID", "ItemType1", item.ItemTypeID);
return View(item);
}
}
As you can see I use var fileName = Path.GetFileName(file.FileName); and item.ImageURL = "/Uploads/" + file.FileName; To populate my ImageUrl. The file itself is saved on the server.
Now my issue is that I don't know how to get the "Edit" view and ActionResult to allow me either to keep the same image or upload a new image.
How can I do this?
What I have done so far is in the Edit view to write:
<div class="form-group">
#if (Model.ImageURL == null)
{
<fieldset>
<legend>Upload a file</legend>
<div class="editor-field">
#Html.TextBox("file", "", new { type = "file" })
</div>
</fieldset>
}
else
{
/*Keep or upload new image here*/
}
</div>
What would I need to write in that else {...} and what would I need to write in the controller's public ActionResult Edit(int? id) {...} ?
Just show Image in the img control and below that use another file control , in case user need to change the uploaded image..
<div class="form-group">
#if (Model.ImageURL == null)
{
<fieldset>
<legend>Upload a file</legend>
<div class="editor-field">
#Html.TextBox("file", "", new { type = "file" })
</div>
</fieldset>
}
else
{
/*show Image in an img control using image path*/
<img src="#Model.ImageURL" height="100" width="100" />
<fieldset>
<legend>change the file</legend>
<div class="editor-field">
#Html.TextBox("file", "", new { type = "file" })
</div>
</fieldset>
}
</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>
and at Action part everything will be same , remove the old file and save new one and update db as well.
also Don't forget to use form as you have used in the create view.
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
if image is not uploaded For the particular item How you will edit . You can do create and edit both in single action method by
var itemInDb = db.items
.Where(c => c.itemid== id) // or whatever your key is
.SingleOrDefault();
if (itemInDb != null)
db.Countries.ApplyCurrentValues(item);
else
db.Countries.AddObject(item);
db.SaveChanges();
if multiple images are there for single image you can loop it
I am attempting to pass a IList of HttpPostedFileBase objects and a "Post" object to an Action. I receive the Post object just fine but my IList is always empty.
See code below...
Controller Action:
[HttpPost]
public ActionResult Create(Post post, IList<HttpPostedFileBase> attachments)
{
if (ModelState.IsValid)
{
var attachmentCounter = attachments.Count;
post.SubmissionDateTime = DateTime.Now;
db.Posts.AddObject(post);
db.SaveChanges();
return RedirectToAction("Index", new { category = post.Category });
}
return View(post);
}
View:
#using (Html.BeginForm("Create", "Posts", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Post</legend>
#Html.HiddenFor(model => model.Category)
#Html.HiddenFor(model => model.Author)
<div class="editor-label">
<label>Photo Attachments:
<span style="color:#666; font-size:12px; font-weight:normal;">(Optional)</span>
</label>
</div>
<div class="editor-field">
#Html.Raw(FileUpload.GetHtml(
name: "attachments",
initialNumberOfFiles: 1,
allowMoreFilesToBeAdded: true,
includeFormTag: false,
addText: "Add another photo...",
uploadText: "").ToString().Replace("<input value\"\" type=\"submit\" />", ""))
</div>
<br />
<p>
<input type="submit" value="Create" class="createButton" style="font-weight:normal;" /> |
#Html.ActionLink("Back to List", "Index", null, new { category = Model.Category }, null)
</p>
</fieldset>
}
I was trying to reproduce your issue, but in my environment all is working. But anyway check once again that your name property in FileUpload exactly the same as second parameter property in controller (even better copypaste it). The Second thing you need to check is the size of file you uploading to the server. If it's bigger then request size allowed in your web config, values will be null.
Also you could check in your method value of Request.Files if it's empty that will means that your file do not even uploading to server. If file will be available, you could get it from there.
I have a scenario where i want one value "tab" to be taken from one action to its view and then from view to another action. I have put the tab value in viewdata to be accessed in view.
Please suggest how do i access this "tab" value in view and then forward it to the action "Authenticate".
I am working on mvc3 2010. Below is my code:
public ActionResult Index(string tab)
{
try
{
ViewData["tab"] = tab;
return View("Authorize");
}
catch (Exception ex)
{
return View("EmptySearch");
}
}
#using (Html.BeginForm("Authenticate", "Authorization"))
{
<div>
<fieldset>
<legend>User Information</legend>
<div class="editor-label">
#Html.Label("Password")
#Html.TextBox("password")
#Html.ValidationMessageFor(m => m.password)
</div>
<p>
<input type="submit" value="Submit" />
</p>
</fieldset>
</div>
}
[AcceptVerbs("POST")]
public ActionResult Authenticate(FormCollection collection)
{
try
{
string password = collection["password"];
password = password.Trim();
//ViewData["BatchNumber"] = password;
//dynamicsContext.CommandTimeout = 180;
//List<BatchMember> batchMemberList =
// Queries.compiledBatchQuery(dynamicsContext, password).ToList<BatchMember>();
return RedirectToAction("Index", "GreatPlains");
}
catch (Exception ex)
{
return View("EmptySearch");
}
}
Keep it in a Hidden field in your Form and it will be available in your Post action method
#using (Html.BeginForm("Authenticate", "Authorization"))
{
<div>
<fieldset>
<legend>User Information</legend>
<div class="editor-label">
#Html.Label("Password")
#Html.TextBox("password")
#Html.ValidationMessageFor(m => m.password)
</div>
<p>
#Html.Hidden("Tab", ViewData["tab"])
<input type="submit" value="Submit" />
</p>
</fieldset>
</div>
}
And in your HttpPOST Action method
[HttpPost]
public ActionResult Authenticate(FormCollection collection)
{
var tabValue=collection["Tab"]
//remaining code
}
You can use TempData, Session or cookie, or you can add the value of tab as a hidden field in your form and keep using ViewData.