How to make file upload required in asp.net mvc? - asp.net-mvc

I am uploading file using asp.net mvc with file upload required but unable to upload file using this. How to make file upload required with validation using ASP.NET MVC?
Here is my Model class code.
public class Slider
{
public int SliderId { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string FileURL { get; set; }
}
Here is my Create Controller:
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create([Bind(Include = "SliderId,Title,FileURL")] HttpPostedFileBase file, Slider slider)
{
if (ModelState.IsValid)
{
if (file != null)
{
string fil = System.IO.Path.GetFileName(file.FileName);
string path = System.IO.Path.Combine(Server.MapPath("~/Content/Uploads/Slider/"), fil);
file.SaveAs(path);
slider.FileURL = "/Content/Uploads/Slider/" + file.FileName;
}
db.Sliders.Add(slider);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(slider);
}
Here is my View:
#model Test.Models.Slider
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("Create", "SliderManager", FormMethod.Post, new { enctype = "multipart/Form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>Create</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Title,"Title*", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-2">
<label for="file">Upload Image for Slide*:</label>
</div>
<div class="col-md-10">
<input type="file" name="file" id="file" style="width:50%" />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}

Changes to make
1st model
public class Slider
{
public int SliderId { get; set; }
[Required]
public string Title { get; set; }
public string FileURL { get; set; }
}
Removed required on file Url as this is not coming from user but you should be populating it
2nd Upload action
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateInput(false)]
public ActionResult Create(HttpPostedFileBase file, Slider slider)
{
//Add validation if file is not present and fail model
if (file == null)
{
ModelState.AddModelError("FileURL", "Please upload file");
}
if (ModelState.IsValid)
{
if (file != null)
{
string fil = System.IO.Path.GetFileName(file.FileName);
string path = System.IO.Path.Combine(Server.MapPath("~/Content/Uploads/Slider/"), fil);
file.SaveAs(path);
slider.FileURL = "/Content/Uploads/Slider/" + file.FileName;
}
//db.Sliders.Add(slider);
//db.SaveChanges();
return RedirectToAction("Index");
}
return View("~/Views/Home/Index.cshtml", slider);
//return View(slider);
}
Also I am not sure why you have specified additional bindings, but I guess you had some reason for that
3rd the view
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { enctype = "multipart/Form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h2>Create</h2>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Title, "Title*", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-2">
<label for="file">Upload Image for Slide*:</label>
</div>
<div class="col-md-10">
<input type="file" name="file" id="file" style="width:50%" />
#Html.ValidationMessageFor(x=>x.FileURL)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
I have added validation message. This validation message of course can have its own property and I would modify it so it fits with your business logic.
<input type="file" name="file" id="file" style="width:50%" />
#Html.ValidationMessageFor(x=>x.FileURL)

I think the solution from this tutorial is better because it doesn't need an extra property:
https://www.aspsnippets.com/Articles/Fileupload-validation-using-Model-Data-Annotations-in-ASPNet-MVC.aspx
Model:
public class FileModel
{
[Required(ErrorMessage = "Please select file.")]
public HttpPostedFileBase PostedFile { get; set; }
}
View:
#model FileUpload_Validation_MVC.Models.FileModel
#{
Layout = null;
}
<div>
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<span>Select File:</span>
#Html.TextBoxFor(m => m.PostedFile, new { type = "file"})
<br/>
#Html.ValidationMessageFor(m => m.PostedFile, "", new { #class = "error" })
<hr/>
<input type="submit" value="Upload"/>
}
</div>

Related

One model for two submit buttons in ASP.NET MVC

I have a form on my view:
#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.DateFrom, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateFrom, new { htmlAttributes = new { #class = "form-control date-picker" } })
#Html.ValidationMessageFor(model => model.DateFrom, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DateTo, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DateTo, new { htmlAttributes = new { #class = "form-control date-picker" } })
#Html.ValidationMessageFor(model => model.DateTo, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" formAction=#Url.Action("CreateReport") />
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EMail, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.EMail, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.EMail, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Send to email" class="btn btn-default" formAction=#Url.Action("SendEmail") />
</div>
</div>
</div>
}
As you can see I have two butons, first button call CreateReport action and than Send button call SendEmail action. I want to create report and then send this report by e-mail.
Here is my controller actions:
public ActionResult Index()
{
Report
report=ReportRepository.GetReport(DateTime.Parse("02.08.1996"), DateTime.Parse("07.08.1996"));
return View(report);
}
public ActionResult CreateReport(Report report)
{
report = ReportRepository.GetReport(report);
return View("Index", report);
}
public ActionResult SendEmail(Report report)
{
return View("Index", report);
}
And my model class:
public class Report
{
public DateTime DateFrom { get; set; }
public DateTime DateTo { get; set; }
public List<OrderDetails> Orders { get; set; }
[Display(Name = "Email address")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string EMail { get; set; }
}
So I mean that I fill Orders list in CreateReport action and display it and after it I press "Send to email" button, that's call "SendEmail" action, where I save Orders list to file and send it.
The problem is that in "SendEmail" action List is null.
How can I fix it?
The simplest way that I could think of is to remove your submit action for create report and handle this with ajax call. So that you will have only one submit action.
Or else you can try with 2 forms in your View.
Personally, I prefer the 1st option.
I'v found a solution. The solution is not to pass model to controller but store my List in Session. Like this:
public ActionResult Index()
{
Report report=ReportRepository.GetReport(DateTime.Parse("02.08.1996"), DateTime.Parse("07.08.1996"));
Session["Orders"] = report.Orders;
return View(report);
}
public ActionResult CreateReport(Report report)
{
report = ReportRepository.GetReport(report);
Session["Orders"] = report.Orders;
return View("Index", report);
}
public ActionResult SendEmail(Report report)
{
List<OrderDetails> orders = (List<OrderDetails>)Session["Orders"];
report.Orders = orders;
return View("Index", report);
}

Submitting image path to the database

Im looking enter in some data and then save it into a DB table,The other fields work just fine when submitted to the database. but when it comes to saving the image path it gives null. Ive searched online and everything i find is out of date and doesn't work for the most part.
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Insert([Bind(Include ="id, ImageName, ImageSize")] ImageInfo info, HttpPostedFileBase ImagePath)
{
if(ModelState.IsValid)
{
if (ImagePath != null)
{
var filename = Path.GetFileName(ImagePath.FileName);
var path = Path.Combine(Server.MapPath("~/Uploads"), filename);
ImagePath.SaveAs(path);
ImagePath.SaveAs(HttpContext.Server.MapPath("~/Uploads") + ImagePath.FileName);
info.ImagePath = ImagePath.FileName;
}
db.ImageInfoes.Add(info);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(info);
}
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Image</legend>
<div class="editor-label">
<div class="form-horizontal">
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ImageName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ImageName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ImageName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ImageSize, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ImageSize, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ImageSize, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#*#Html.TextBoxFor(model => model.ImagePath, new { type = "file" })*#
<input type="file" name="ImagePath" id="ImagePath" />
#Html.ValidationMessageFor(model => model.ImagePath, "", new { #class = "text-danger" })
</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>
</fieldset>
DB
Any help would be great!.
You want to add enctype="multipart/form-data" to form tag.
I personally like to rename HttpPostedFileBase parameter name to file not to confuse with ImageInfo.ImagePath.
View
#using (Html.BeginForm("Index","Start",FormMethod.Post,new {enctype="multipart/form-data"}))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Image</legend>
<div class="editor-label">
<div class="form-horizontal">
<hr/>
...
<div class="form-group">
#* Notice name and id are named as file *#
<input type="file" name="file" id="file"/>
#Html.ValidationMessageFor(model => model.ImagePath, "", new {#class = "text-danger"})
</div>
...
</div>
</div>
</fieldset>
}
Model
public class ImageInfo
{
public string Id { get; set; }
public string ImageName { get; set; }
public string ImageSize { get; set; }
public string ImagePath { get; set; }
}
Action Method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "id, ImageName, ImageSize")] ImageInfo info,
HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
if (file != null)
{
file.SaveAs(Server.MapPath("~/Uploads") + file.FileName);
info.ImagePath = file.FileName;
}
/*db.ImageInfoes.Add(info);
db.SaveChanges();*/
return RedirectToAction("Index");
}
return View(info);
}

insert many images which are in icollection in other model in asp mvc

My first model
public class article
{
public int id { set; get; }
public string title { set; get; }
public string bodyofarticle { set; get; }
public ICollection <image> images { set; get; }
}
The second model which I use to store the images URL in the database
public class image
{
public int id { set; get; }
public int articleid { set; get; }
public string url{ set; get; }
}
Form to enter the values its okay to enter the
article model but when I put any image it did not insert into the database
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>article</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.title, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.title, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.title, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.bodyofarticle, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.TextAreaFor(model => model.bodyofarticle, new { htmlAttributes = new { #class = "form-control" }, cols = 60, rows = 10 } )
#Html.ValidationMessageFor(model => model.bodyofarticle, "", new { #class = "text-danger" })
</div>
<input type="file" multiple id="file" name="file" />
</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>
}
When I look at the break point I see that files are empty and I have tried many ways but it's still empty
public ActionResult Create([Bind(Include = "id,title,bodyofarticle")] article article , HttpPostedFileBase files)
{
if (ModelState.IsValid)
{
if (files != null && files.ContentLength >0 )
{
foreach (string file in Request.Files)
{
var v = Request.Files[file];
var fileName = Path.GetFileName(files.FileName);
var path = Path.Combine(Server.MapPath("~/images/"), fileName);
files.SaveAs(path);
image imag = new image();
imag.url = Url.Content("~/images/" + fileName);
article.images.Add(imag);
}
}
db.articles.Add(article);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(article);
}
Try this in the view. You will need to post the file from view.
#using (Html.BeginForm("Create", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
Hope you used the command add-migrations before using the update-database command.
//it was at the view
#using (Html.BeginForm("Create", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
//thnx vikrim and jim

Upload a HttpPostedFileBase for image but always null

I am new to MVC and web programming.
My uploading image is working fine, but when I use almost the exact same code to allow user to modify its upload, it HttpPostedFileBase is always null. Driving me crazy...
here is the model
public class ModifyGenreViewModel
{
public int Id { get; set; }
[Display(Name = "Nom du style")]
public string Name { get; set; }
[Display(Name = "Image")]
public HttpPostedFileBase Image { get; set; }
}
and the view
#using (Html.BeginForm("ModifyGenre", "Upload", null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>#Resource.StyleCreationHeader</h4>
<hr />
#Html.ValidationMessageFor(model=>Model)
<div class="form-group" style="display:none">
#Html.LabelFor(model => model.Id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.EditorFor(model => model.Id, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Image, new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" data-val="true" id="ModifyGenreViewModel_Image" name="ModifyGenreViewModel.Image" />
#Html.ValidationMessageFor(model => model.Image, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="#Resource.Modify" class="btn btn-primary" />
</div>
</div>
</div>
}
When I set the breakpoint in my controller, I see the Id and the name, but Image is always null.
Thank's for your help!
The name property value of your file input should match with the view model property name for model binding to work properly.
Change the input field name to "Image"
<input type="file" data-val="true" id="ModifyGenreViewModel_Image" name="Image" />
Assuming your HttpPost action method accepts the ModifyGenreViewModel object as parameter.
[HttpPost]
public ActionResult ModifyGenre(ModifyGenreViewModel model)
{
// to do : return something
}
In model class change it to public String Image { get; set; }
In .cshtml page change it to #Html.TextBoxFor(m => m.Image, new {
type = "file", name="Image" })
In Controller you should use the name what you have decleared in html helper HttpPostedFileBase Image

MVC strongly typed view data

I have a strongly typed view that creates the form fields like this:
#Html.TextBoxFor(x => x.Name, new { #class = "form-control" })
That's fine, X in the above lambda expression relates to a class I've created with a property of 'Name'.
How do I use this same view but have access to the properties of a different class? For example if you imagine I've another class called - UserDetails, and email address is a property of that. How can I do this:
#Html.TextBoxFor(x => x.Email, new { #class = "form-control" })
within the same strongly typed view?
In asp.net mvc you pass model to view like this:
public ActionResult Base()
{
return View(new DerviedOne());
}
And here is your models definitions :
public class BaseModel
{
public int Id { get; set; }
}
public class DerviedOne : BaseModel
{
public string Email { get; set; }
}
public class DerviedTwo : BaseModel
{
public string Name { get; set; }
}
Then you have to create three views:
Base View:
#using Models
#model Models.BaseModel
#{
ViewBag.Title = "Base";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Base</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>BaseModel</h4>
<hr/>
#Html.ValidationSummary(true, "", new {#class = "text-danger"})
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
#Html.TextBoxFor(x=>x.Id)
<input type="submit" value="Create" class="btn btn-default"/>
</div>
</div>
</div>
if(Model is DerviedOne)
{
Html.RenderPartial("DerviedOneView", Model as DerviedOne);
}
if (Model is DerviedTwo)
{
Html.RenderPartial("DerviedTwoView", Model as DerviedTwo);
}
}
Second view:
#model WebApplication.Models.DerviedOne
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DerviedOne</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Email, "", new { #class = "text-danger" })
</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>
}
Third view:
#model WebApplication.Models.DerviedTwo
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DerviedTwo</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(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="Create" class="btn btn-default" />
</div>
</div>
</div>
}

Resources