I have an ASP.NET MVC website. I need a page where the user must enter several fields, including an image file.
I could find many, many references for uploading a file using MVC. But they don't upload the file as part of a form with other fields.
Ideally, fields and file will be sent to a single controller. Any tips?
If you do not use third party libraries, try this:
Model
public class Strategy
{
public int ID { get; set; }
public string Name { get; set; }
public byte[] File { get; set; }
}
View
#model TEST.Model.Strategy
#using (Html.BeginForm("Add", "Strategy", FormMethod.Post, new { #id = "frmStrategy", enctype = "multipart/form-data" }))
{
#Html.TextBoxFor(x => x.Name)
<input id="templateFile" name="templateFile" type="file" />
#Html.HiddenFor(x => x.ID)
}
Controller
[HttpPost]
public ActionResult Add(Strategy model, HttpPostedFileBase templateFile)
{
if (templateFile != null && templateFile.ContentLength > 0)
{
try
{
var fname = Path.GetFileName(templateFile.FileName);
using (MemoryStream ms = new MemoryStream())
{
templateFile.InputStream.CopyTo(ms);
byte[] array = ms.GetBuffer();
model.File = array;
}
...
You can use FineUploader. See Demo
Valums Uploader. It uses pure Javascript (uploads file using Iframe)
You might need to use a client plugin. Plupload is one possible choice. And here's an example of how you could integrate it in your MVC application. Another popular plugin which supports this functionality is Uploadify.
Asp.net mvc 3 file uploads using the fileapi
See Progress Demo 1, 2 & 3 at http://jquery.malsup.com/form/#file-upload
Ref: http://forums.asp.net/t/1897410.aspx/1?MVC4+File+Upload
Related
I'm trying to get a multiselect dropdown feature implemented for my BugTracker, and can't seem to find a solution that doesn't involve creating another model. Here's how it works:
I have a model named Report, which contains information about a bug. This model contains fields such as bug name, urgency, date submitted, solution, etc. What I want is to add a field that would actually be a list of related bugs. Thus, when the programmer is updating this report, he can select from a list of previous bugs (reports) and add multiple report numbers. When the report is viewed (not edited), these numbers will appear as an ActionLink to the report page. So maybe the user sees this:
RelatedBugs: 1, 37, 56
And the code behind it would be like this:
RelatedBugs: #Html.ActionLink("1", "Admin", "Report", 1, null), #Html.ActionLink("37", "Admin", "Report", 37, null), #Html.ActionLink("56", "Admin", "Report", 56, null)
(Where these numbers are actually the ID of the Report.cs from below.)
So on the Edit View of the form, there would be a multiselect dropdown where you can pick all of these bugs. This would be similar to https://harvesthq.github.io/chosen/, where in the multiselect area you can see a list of all the bugs/reports and their associated IDs. Or even just a list of the IDs!
And on the typical view, you'd see what you have above.
What can I do to get this working properly?
Let me give you my own code to show you what I mean (code significantly cut to make the question easier):
Report.cs (model)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace BugTracker.Models
{
public class Report
{
//For User
public int ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public String Solution { get; set; }
}
}
AdminReport.cshtml (View)
#model BugTracker.Models.Report
#{
ViewBag.Title = "Report";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>#Model.Name</h1>
Description: #Model.Description <br /> <br />
Solution: #Model.Solution <br /> <br>
And the form used for editing...
AdminReportForm.cshtml
#model BugTracker.ViewModels.UserReportViewModel
#{
ViewBag.Title = "Issue Form";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h1>Update Issue</h1>
#Model.Report.Name <br />
#using (Html.BeginForm("SaveAdmin", "Report"))
{
<div class="form-group">
<label>Solution</label>
#Html.TextAreaFor(m => m.Report.Solution, new { #class = "form-control", #rows="10"})
</div>
#Html.HiddenFor(m => m.Report.ID)
<button type="submit" class="btn btn-primary">Save</button>
}
My viewmodel (the real ViewModel contains several additional fields):
UserReportViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using BugTracker.Models;
using System.Web.Mvc;
namespace BugTracker.ViewModels
{
public class UserReportViewModel
{
public Report Report { get; set; }
}
}
Controllers...
(For AdminReport.cshtml)
public ActionResult Admin(int id)
{
var report = _context.Reports.SingleOrDefault(c => c.ID == id);
if (report == null)
return HttpNotFound();
var viewModel = new UserReportViewModel
{
Report = report,
};
return View("AdminReport", report);
}
For AdminReportForm.cshtml
public ActionResult EditAdmin(int id)
{
var report = _context.Reports.SingleOrDefault(c => c.ID == id);
if (report == null)
return HttpNotFound();
var viewModel = new UserReportViewModel
{
Report = report,
};
return View("AdminReportForm", viewModel);
}
And finally my Post:
[HttpPost]
[ValidateInput(false)]
public ActionResult SaveAdmin(UserReportViewModel viewModel)
{
if (viewModel.Report.ID == 0)
_context.Reports.Add(viewModel.Report);
else
var reportInDb = _context.Reports.Single(c => c.ID == viewModel.Report.ID);
_context.SaveChanges();
return RedirectToAction("ReportAll", "Report");
}
You can add a Collection of Reports to your view model?
Then you can use linq to filter the reports and populate the Dropdown.
namespace BugTracker.ViewModels
{
public class UserReportViewModel
{
public Report Report { get; set; }
public List<Report> Reports {get; set;}
}
}
I was able to finally resolve this issue - thanks for all the help, everyone! Here was how I resolved it:
First, in my "Report.cs" file, I added the following:
public String RelatedBugs {get; set;}
public String[] RelatedBugsArray {get; set;}
In my view model "UserReportViewModel.cs", I added the following:
public IEnumerable<Report> Report {get; set;}
The form gave me some issues. I initially used a DropDownBoxFor, but for multiselect you actually need to use ListBoxFor (it will still work with the DropDownBoxFor, but the problem is it can't pre-populate it when you try to edit the form.) So, in AdminReportForm.cshtml, I have the following:
#Html.ListBoxFor(m => m.Report.RelatedBugsArray, new MultiSelectList(Model.Reports, "ID", "Name"), new {id = "relatedbugs"})
To use the chosen script, I first set it up by using this link: Use Chosen jQuery plugin in MVC Asp Net
In the same report from, I made my ListBoxFor utilize my Chosen script with this:
#section scripts{
<script>
$(function () {
$("#relatedbugs").chosen();
});
</script>
}
Now, all that is left is the controller. There are two actions I care about: the edit and the save action. The EditAdmin and Admin actions will have the same code:
if (report.RelatedBugs != null)
report.RelgatedBugsArray = report.RelatedBugs.Split(',');
For the SaveAdmin action, we just need to do this:
if (viewModel.Report.RelatedBugsArray == null)
viewModel.Report.RelatedBugs = null;
else
viewModel.Report.RelatedBugs = string.Join(",", viewModel.Report.RelatedBugsArray);
Basically, what we are doing is taking the Report IDs as an array and putting them into a string with a comma between each element. When we go to feed it back to the view, convert the string to an array by splitting each element between the commas.
Thanks again for all the suggestions!
I hope my title is understandable. I will try to be as clear as possible. So here's what I am planning to do. I have a Customer Info page which they can upload their documents, in the page, there is two input files which they can upload their documents and then the moment they click submit, I want to fill the Photo and PassportNo variable with the path string so I could retrieve the file later.
What I am struggle with currently is that I manage to upload the files to the system, but I am kinda stuck with updating the filePath into the Photo and PassportNo properties. I can update one of them however I dont know how to retrieve two Input Files. What I meant is that, when the Form Submit, I dont know how to retrieve which type of Input Types are coming from the form (Photo or Passport No).
I want to retrieve the "name" from the Input to the Controller, so in the Controller I can do something like "If its coming from the "name", then do certain update". Hope I am clear enough, if there are other suggestion please feel free to suggest.
Thanks!
Customer Model
public int Id { get; set; }
public string Name { get; set; }
public string Photo { get; set; }
public string PassportNo { get; set; }
View
#using (Html.BeginForm("UploadFiles", "Customer", FormMethod.Post, new {enctype= "multipart/form-data"}))
{
<input type="file" name="Photo" />
<input type="file" name="PassportNo" />
<input type="submit" name="submit" value="Upload Files" />
}
Controller
[HttpPost]
public ActionResult UploadFiles(HttpPostedFileBase Photo)
{
if (Photo.ContentLength > 0)
{
var filename = Path.GetFileName(Photo.FileName);
var path = Path.Combine(Server.MapPath("~/Files/"), filename);
Photo.SaveAs(path);
}
return RedirectToAction("FileForm");
}
just add second parameter to the action
public ActionResult UploadFiles(HttpPostedFileBase Photo, HttpPostedFileBase PassportNo)
I am creating an application in mvc4. In edit mode i want user to edit his/her details,
user have option to change his profile image by selecting from fileuploader. But if user do not select file in uploader the previous file location will be sent.
I am storing image path in table same as other details.
So, only one Stored procedure is created.
I am only 1 day old in mvc.Started working on mvc directly, without studying by seniors order.
So pls help
It is a good practice to use strongly typed models. You should create a separater ViewModel for your User:
Models/UserEditViewModel
public int Id { get; set; }
public string Name { get; set; }
// Other properties
public HttpPostedFileBase Photo { get; set; }
Controller:
[HttpGet]
public ActionResult Edit(int id)
{
// Get user from database
var user = db.Users.Find(id);
// Map user to UserEditViewModel
var userVM = new UserEditViewModel
{
Id = user.Id;
Name = user.Name;
// Other mappings
}
return View(userVM);
}
[HttpPost]
public ViewResult Edit(UserEditViewModel model)
{
if (ModelState.IsValid)
{
var existingUser = db.Users.Find(model.Id);
if (model.Photo != null && model.Photo.ContentLength > 0)
{
// Update database and copy image to server
}
}
}
View:
#model YourProject.Models.UserEditViewModel
#using (Html.BeginForm("Edit", "YourController", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
// Form stuff
#Html.HiddenFor(m => m.Id)
#Html.EditorFor(m => m.Name)
#Html.EditorFor(m => m.Photo) // Creates <input type="file" name="Photo" />
<input type="submit" value="Save" />
}
The following code is on the assumption that you save the file's name in the db table and save the file on the app server.
The code will be as follows:
Model:
public string Photo {get;set;}
View :
<input type='file' id='file'/>
#Html.HiddenFor(m=>m.Photo)
JS :
// if there is a photo update by user, change the Photo hidden element value
//get the final name of the photo saved in server
$("#Photo").val(newname);
Then simply post your form.
In case, the user doesn't change its photo, then the previous value will remain in the hidden element and updated to the db. Hence, nothing will actually change in the photo column.
I strongly recommend this tutorial for MVC beginners.
How do I upload images to go into ~/Content/Images in ASP.NET MVC 3.0?
HTML Upload File ASP MVC 3.
Model: (Note that FileExtensionsAttribute is available in MvcFutures. It will validate file extensions client side and server side.)
public class ViewModel
{
[Required, Microsoft.Web.Mvc.FileExtensions(Extensions = "csv", ErrorMessage = "Specify a CSV file. (Comma-separated values)")]
public HttpPostedFileBase File { get; set; }
}
HTML View:
#using (Html.BeginForm("Action", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.TextBoxFor(m => m.File, new { type = "file" })
#Html.ValidationMessageFor(m => m.File)
}
Controller action:
[HttpPost]
public ActionResult Action(ViewModel model)
{
if (ModelState.IsValid)
{
// Use your file here
using (MemoryStream memoryStream = new MemoryStream())
{
model.File.InputStream.CopyTo(memoryStream);
}
}
}
If you want to upload images in ASP.NET MVC, try these questions:
Simple Image Upload in ASP.NET MVC
Uploading an image in ASP.NET MVC
If you're wanting to use an ASP.NET control to upload images, that breaks the separation of concerns for MVC. There are upload helpers available.
Hey...
I have upload control on my view. Is there a way to associate this control with model data(something like LabelFor or TextBoxFor). I need this, because on page load I loose my information in file upload control
Thx
HTML Upload File ASP MVC 3.
Model: (Note that FileExtensionsAttribute is available in MvcFutures. It will validate file extensions client side and server side.)
public class ViewModel
{
[Required, Microsoft.Web.Mvc.FileExtensions(Extensions = "csv", ErrorMessage = "Specify a CSV file. (Comma-separated values)")]
public HttpPostedFileBase File { get; set; }
}
HTML View:
#using (Html.BeginForm("Action", "Controller", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.TextBoxFor(m => m.File, new { type = "file" })
#Html.ValidationMessageFor(m => m.File)
}
Controller action:
[HttpPost]
public ActionResult Action(ViewModel model)
{
if (ModelState.IsValid)
{
// Use your file here
using (MemoryStream memoryStream = new MemoryStream())
{
model.File.InputStream.CopyTo(memoryStream);
}
}
}
Yes, use the HttpPostedFileBase class for the property type and it will bind just like any other property would.