Upload Image in Asp.Net Core? - asp.net-mvc

I want to upload image in "wwwroot/uploads/img" folder but i get error.I wrote the following code:
Create View :
#model imageuploader.Models.Employee
<form method="post" enctype="multipart/form-data" asp-controller="Employee" asp-action="Create">
<div class="form-group">
<div class="col-md-10">
<input asp-for="FirstName" class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input asp-for="LastName" Class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input asp-for="ImageName" type="file" Class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Create" />
</div>
</div>
Model :
public class Employee
{
[Key]
public int ID { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string ImageName { get; set; }
}
Controller
private readonly RegisterDBContext _context;
private readonly IHostingEnvironment _appEnvironment;
public EmployeeController(RegisterDBContext context, IHostingEnvironment appEnvironment)
{
_context = context;
_appEnvironment = appEnvironment;
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Employee emp)
{
if (ModelState.IsValid)
{
var files = HttpContext.Request.Form.Files;
foreach (var Image in files)
{
if (Image != null && Image.Length > 0)
{
var file = Image;
//There is an error here
var uploads = Path.Combine(_appEnvironment.WebRootPath, "uploads\\img");
if (file.Length > 0)
{
var fileName = Guid.NewGuid().ToString().Replace("-", "") + Path.GetExtension(file.FileName);
using (var fileStream = new FileStream(Path.Combine(uploads, fileName), FileMode.Create))
{
await file.CopyToAsync(fileStream);
emp.BookPic = fileName;
}
}
}
}
_context.Add(emp);
await _context.SaveChangesAsync();
return RedirectToAction("Index");
}
else
{
var errors = ModelState.Values.SelectMany(v => v.Errors);
}
return View(emp);
}
When i click on submit button i get an error (error line is marked), how can i upload image or file in Specified path?
Error:
NullReferenceException: Object reference not set to an instance of an object.
imageuploader.Controllers.EmployeeController+<Create>d__2.MoveNext() in EmployeeController.cs
var uploads = Path.Combine(_appEnvironmen.WebRootPath, "uploads\\img\\");
How can i upload image correctly in Specified path?

I Solved it. I understood i initial bad
_appEnvironment
in Constructor.
With repeated edits, all of the codes in question are currently correct.
Thanks #Shyju user.

Here is how to upload an image in C# Asp.net core Web Application 2.1 MVC:
First I'm assuming you've created a model, added to db and now working in the controller.
I've created a Person model
person model
For create get:
//Get : Person Create
public IActionResult Create()
{
return View();
}
post Action (please see screen shot)
create post action method
Finally the view
Create View
Project Output:
Here is the output of my project

Related

EF Core ModelSate Invalid because form is passing foreign key name and value attributes

Very new to MVC Core and C# and just as I think I'm getting the hang of something there's a new curve ball. I have a form which is based on a model which has a foreign key. When I submit the form to the controller the modelState is invalid because the form is passing something back which isn't in the model it is based on. Here is the model:
public partial class Agreement
{
public Agreement()
{
AgreementAmendments = new HashSet<AgreementAmendment>();
Bundles = new HashSet<Bundle>();
Invoices = new HashSet<Invoice>();
}
public int Id { get; set; }
public int OrgId { get; set; }
public string AgreementNumber { get; set; } = null!;
public string? IrespondReference { get; set; }
public string? DocumentLink { get; set; }
public virtual Organization Org { get; set; }
public virtual ICollection<AgreementAmendment> AgreementAmendments { get; set; }
public virtual ICollection<Bundle> Bundles { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
This is the Get Create Action Method:
public IActionResult Create()
{
ViewData["OrgId"] = new SelectList(_context.Organizations, "Id", "ShortName");
return View();
}
This is the form:
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="OrgId" class="control-label">Organization</label>
<select asp-for="OrgId" class ="form-control" asp-items="ViewBag.OrgId"></select>
</div>
<div class="form-group">
<label asp-for="AgreementNumber" class="control-label">Agreement Number</label>
<input asp-for="AgreementNumber" class="form-control" />
<span asp-validation-for="AgreementNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="IrespondReference" class="control-label">Internal Reference</label>
<input asp-for="IrespondReference" class="form-control" />
<span asp-validation-for="IrespondReference" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="DocumentLink" class="control-label">Document Link</label>
<input asp-for="DocumentLink" class="form-control" />
<span asp-validation-for="DocumentLink" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
And this is the HttpPost Create Action Method:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("OrgId,AgreementNumber,IrespondReference,DocumentLink")] Agreement agreement)
{
if (ModelState.IsValid)
{
_context.Add(agreement);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["OrgId"] = new SelectList(_context.Organizations, "Id", "Id", agreement.OrgId);
return View();
}
When I look at the results of the ModelState it shows an error with the Org Key but as far as I can see the form should just be returning the OrgId as per the model. Can someone please let me know where I am going wrong.
Created a View Model for Agreements to handle the form input and then passed that to the base Agreement Model which seems like unnecessary work. Why can't EF Core handle this stuff without having to constantly build View Models just because there is a foreign key?
Anyway, this is the final HttpPost code for others who run into the same issue:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(AgreementWriteViewModel newagreement)
{
if (ModelState.IsValid)
{
var model = new Agreement
{
OrgId = newagreement.OrgId,
AgreementNumber = newagreement.AgreementNumber,
IrespondReference = newagreement.IrespondReference,
DocumentLink = newagreement.DocumentLink,
};
_context.Add(model);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["OrgId"] = new SelectList(_context.Organizations, "Id", "ShortName", newagreement.OrgId);
return View();
}

Problem uploading member image in Umbraco

I am working with member API in Umbraco 8 and i am trying to upload the image when registering. After i register member and open member profile in backoffice i see this:
This is my Coontroller
public class RegisterController : SurfaceController
{
// GET: Register
public ActionResult Register(Models.RegisterModel model)
{
if (!ModelState.IsValid)
return CurrentUmbracoPage();
var memberService = Services.MemberService;
if (memberService.GetByEmail(model.Email) != null)
{
ModelState.AddModelError("", "A member with that email alredy exists");
return CurrentUmbracoPage();
}
var member = memberService.CreateMemberWithIdentity(model.Email, model.Email, model.Name, "bMEMembers");
member.SetValue("companyName", model.CompanyName );
member.SetValue("avatar", model.Avatar);
memberService.SavePassword(member, model.Password);
Members.Login(model.Email, model.Password);
memberService.Save(member);
return Redirect("/");
}
}
Model:
public HttpPostedFileBase Avatar { get; set; }
View:
#Html.LabelFor(model => model.Avatar)
<input type="file" name="Avatar" />
#Html.ValidationMessageFor(model => model.Avatar)
<input class="btn" type="submit" value="Create" />
Anybody could help me with this?
It's normal to appear because Avatar is an object (exactly: HttpPostedFileBase), and LabelFor will display object namespace.
Take a look: https://learn.microsoft.com/en-us/dotnet/api/system.web.mvc.html.labelextensions.labelfor?view=aspnet-mvc-5.2
and see that there is an overload for that method:
public static System.Web.Mvc.MvcHtmlString LabelFor<TModel,TValue> (this System.Web.Mvc.HtmlHelper<TModel> html, System.Linq.Expressions.Expression<Func<TModel,TValue>> expression, string labelText, System.Collections.Generic.IDictionary<string,object> htmlAttributes);
you can use like:
#Html.LabelFor(model => model.Avatar, "Avatar", null);
or more beauty way:
[DisplayName("Avatar")]
public HttpPostedFileBase Avatar { get; set; }
and leave your CSHTML code:
#Html.LabelFor(model => model.Avatar)

Displaying List of Uploaded Files

I have a form where users can upload files and then view a list of their uploads. I'm running into two issues:
List of files isn't appearing when page loads. The SQL Query is valid.
When user uploads a file, a NullReferenceException because the file list model isn't being loaded. I'm not sure how to pass this model into view after the upload. Any advice is greatly appreciated.
Controller for fetching list of datasets is below. The controller for uploading datasets is different, of course, but it accepts an HttpPostedFileBase and a datasetName. It only returns ViewBag.error/ViewBag.message.
public ActionResult upload(DatasetViewModel model)
{
List<DatasetDetail> model2 = new List<DatasetDetail>();
var connectionstring = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
using (SqlConnection con = new SqlConnection(connectionstring))
try
{
// Your code
con.Open();
using (SqlCommand cmd = new SqlCommand("", con))
{
cmd.CommandText = "SELECT datasetid, datasetname, timestamp FROM datasets WHERE userid = #userid";
cmd.Parameters.Add("#userid", SqlDbType.Text);
cmd.Parameters["#userid"].Value = System.Web.HttpContext.Current.User.Identity.GetUserId();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
var u = new DatasetDetail();
u.datasetid = reader["datasetid"].ToString();
u.dataset = reader["datasetname"].ToString();
/* u.timestamp = Convert.ToDateTime(reader["TIMESTAMP"]);*/
model2.Add(u);
}
}
}
catch
{
// Catch exception
}
finally
{
// Close the connection
con.Close();
}
model.datasetlist = model2;
return View(model);
}
View:
#model WebApplication12.Models.DatasetViewModel
<div style="width: 320px;">
<h2>Manage Datasets</h2>
Download Excel Template
#using (Html.BeginForm("upload", "Dashboard", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<div class="form-group">
<label>Dataset Name:</label>
<br />
<input type="text" id="datasetname" name="datasetname" />
</div>
<div class="form-group">
<input type="file" id="dataFile" name="upload" />
</div>
<div class="form-group">
<input type="submit" value="submit" class="btn btn-block" />
</div>
}
</div>
<div id="response"></div>
#if (ViewBag.Message != null)
{
<div class="alert alert-success" role="alert">#Html.Raw(ViewBag.Message)</div>
}
#if (ViewBag.Error != null)
{
<div class="alert alert-error" role="alert">#Html.Raw(ViewBag.Error)</div>
}
<div>
#foreach (var u in Model.datasetlist)
{
<b>u.dataset</b>
}
</div>
Relevant Models:
public class DatasetViewModel
{
public List<DatasetDetail> datasetlist { get; set; }
}
public class DatasetDetail
{
public string datasetid { get; set; }
public string dataset { get; set; }
/* public DateTime timestamp { get; set; }*/
}

Updating View with changed ViewModel after Form Post

I'm trying to update an MVC view to display a message after a form has been posted but cannot get it working. I'm new to this so would appreciate a few pointers on what I'm doing wrong. This isn't form validation as the logic and response message won't be known until after the form has been posted and processed.
The controller action [HttpPost] public async Task<IActionResult> PlatformRecord(RecAction RecActionNotUSed) fires on the Form POST and does some codebehind logic - I then need to update the View with a Response which is only a temporary message and not stored anywhere.
The initial GET request works fine and does display the message but just cannot figure out how to do the same after a Form POST.
I've tried adding ModelState.Clear(); without success. Also, if I redirect to the initial view I lose the Response message eg return RedirectToAction("PlatformRecord"); means I no longer have RecAction.Response = "NEED TO SHOW THIS RESPOSNE MESSAGE AFTER FORM POST.";
My code is as follows
VIEWMODEL:
public class RecAction
{
public RecUser RecUser { get; set; }
public string Response { get; set; }
}
CONTROLLER:
public class RecordManagerController : Controller
{
private readonly IOptions<ConnectionStrings> _connectionStrings;
private readonly UserManager<AppRecationUser> _userManager;
Public RecordManagerController(UserManager <AppRecationUser> UserManager,
IOptions <connectionStrings> connectionStrings)
{
_userManager = userManager;
_connectionStrings = connectionStrings;
}
// GET: /<controller>/
public IActionResult Index()
{
return View();
}
private Task<AppRecationUser> GetCurrentUserAsync()
{
return _userManager.GetUserAsync(HttpContext.User);
}
public async Task<IActionResult> PlatformRecord()
{
var RecordDataModel = new RecordDataModel(_connectionStrings.Value.DefaultConnection);
var user = await GetCurrentUserAsync();
RecAction RecAction = new RecAction();
RecAction.RecUser = RecordDataModel.GetRecord(user.Email, "Platform");
if (RecAction.RecUser.Record == null)
{
//Response Successfully Displayed
RecAction.Response = "No Record found";
}
return View(RecAction);
}
[HttpPost]
public async Task<IActionResult> PlatformRecord(RecAction RecActionNotUSed)
{
try
{
if (ModelState.IsValid)
{
var RecordDataModel = new RecordDataModel(_connectionStrings.Value.DefaultConnection);
var user = await GetCurrentUserAsync();
RecAction RecAction = new RecAction();
RecAction.RecUser = RecordDataModel.GetRecord(user.Email, "Platform");
RecSettings latestSettings = RecordDataModel.GetSettings();
RecKeys RecKeys = RecordDataModel.GetKey();
if (RecKeys.PrivateKey == null)
{
ModelState.Clear();
//Rsponse not updating
RecAction.Response = "NEED TO SHOW THIS RESPOSNE MESSAGE AFTER FORM POST.";
return View(RecAction);
}
return RedirectToAction("PlatformRecord");
}
Else
{
//Need to return the same view for errors so the validation is not overwritten.
return View();
}
}
catch
{
// If we got this far, something failed, redisplay form
return RedirectToAction("PlatformRecord");
}
}
}
VIEW:
#model ProjectXYZ.Models.RecordModels.RecAction
#{
ViewData["Title"] = "PlatformRecord";
}
<h2>Platform Record</h2>
<form asp-controller="RecordManager" asp-action="PlatformRecord" asp-route-returnurl="#ViewData["ReturnUrl"]" method="post" class="form-horizontal" autocomplete="off">
<p></p>
<hr />
<div class="form-group">
<label asp-for="RecUser.Record" class="col-md-2 control-label"></label>
<div class="col-md-10">
<textarea asp-for="RecUser.Record" class="form-control" cols="1" rows="8" readonly></textarea>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Response" class="text-danger" readonly/>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">Request New Record</button>
</div>
</div>
</form>

ASP.NET MVC Two file upload, different destinations

I'm trying to upload two different files into two different database fields on same form.
------------------------------------
reportid | name | image | template |
------------------------------------
this is the table look. So I want to upload files to image and template. My model:
public class Report
{
[Key]
public int ReportID { get; set; }
[Required]
public string Name { get; set; }
public byte[] Image { get; set; }
public byte[] Template { get; set; }
}
My Create method in controller:
public ActionResult Create(Report report, HttpPostedFileBase file, HttpPostedFileBase temp)
{
if (ModelState.IsValid)
{
if (file != null && file.ContentLength > 0)
{
using (MemoryStream ms = new MemoryStream())
{
file.InputStream.CopyTo(ms);
report.Image = ms.GetBuffer();
}
}
if (temp != null && temp.ContentLength > 0)
{
using (MemoryStream ms1 = new MemoryStream())
{
temp.InputStream.CopyTo(ms1);
report.Template = ms1.GetBuffer();
}
}
db.Reports.Add(report);
db.SaveChanges();
db.Configuration.ValidateOnSaveEnabled = true;
return RedirectToAction("Index");
}
And part of view concerning uploads:
<div class="editor-label">
<%:Html.LabelFor(model => model.Image) %>
</div>
<div class="editor-field">
<input type="file" id="fuImage" name="file" />
</div>
<div class="editor-label">
<%:Html.Label("Template") %>
</div>
<div class="editor-field">
<input type="file" id="temp" name="temp"/>
</div>
<p>
<input type="submit" value="Create" />
</p>
I'm quite stuck in this since I can not use IEnumerable<HttpPostedFileBase> files as a parameter in Create method because I need to save it in a different field, or can I? How should I approach this? Please help :S
Note: Image upload works fine.
Why not use IEnumerable<HttpPostedFileBase> ? You may use it like this.
[HttpPost]
public ActionResult Create(Report report, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
//Let's take first file
if(files.ElementAt(0)!=null)
{
var file1=files.ElementAt(0);
if (file1!= null && file1.ContentLength > 0)
{
//do processing of first file
}
}
//Let's take the second one now.
if(files.ElementAt(1)!=null)
{
var temp =files.ElementAt(1);
if (temp!= null && temp.ContentLength > 0)
{
//do processing of second file here
}
}
}
//Do your code for saving the data.
return RedirectToAction("Index");
}
EDIT : After seeing your View Markup in your EDIT.
The name of the file input element should be same as the parameter name in your action method. (files in this example)
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<b>File 1</b>
<input type="file" name="files" id="file1" />
<b>File 2</b>
<input type="file" name="files" id="file2" />
<input type="submit" />
}
This code assumes that read ONLY the first 2 entries from the collection.Since you wanted only 2 files, i hardcoded the indexes.
Phil has a nice blog post explaining about it very nicely.

Resources