I use asp.net core razor pages to create my application, in my create page, I have two file controls, one for upload icon image and the other for uploading detail images. But when I click edit button, all of the fields were initialized, except the two file controls. Please check my code. Anyone can help?
In my razor page:
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Product.Icon" class="control-label"></label>
<input asp-for="#Model.Icon" type="file" />
<span asp-validation-for="Product.Icon" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label class="control-label”>Detail Images(support multi-uploading):</label>
<input type="file" id="fUpload" name="files" multiple />
</div>
</div>
</div>
In my page model:
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Product = await _context.Products
.Include(p => p.Shop).SingleOrDefaultAsync(m => m.ID == id);
if (Product == null)
{
return NotFound();
}
ViewData["Shop"] = new SelectList(_context.Shops, "ID", "Name");
return Page();
}
public async Task<IActionResult> OnPostAsync(List<IFormFile> files)
{
if (!ModelState.IsValid)
{
return Page();
}
var uploads = Path.Combine(hostingEnvironment.WebRootPath, "uploads");
if (!Directory.Exists(uploads))
{
Directory.CreateDirectory(uploads);
}
if (this.Icon != null)
{
var fileName = GetUniqueName(this.Icon.FileName);
var filePath = Path.Combine(uploads, fileName);
this.Icon.CopyTo(new FileStream(filePath, FileMode.Create));
this.Product.Icon = fileName;
}
if (files != null && files.Count > 0)
{
foreach (IFormFile item in files)
{
if (item.Length > 0)
{
var fn = GetUniqueName(item.FileName);
var fp = Path.Combine(uploads, fn);
item.CopyTo(new FileStream(fp, FileMode.Create));
this.Product.ProductImages = this.Product.ProductImages + fn + "^";
}
}
}
_context.Attach(Product).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(Product.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
The most important part of your page is missing from the code you provided - the <form> tag. For file uploading to work, you must specify that the method is post and you must also provide an enctype attribute with its value set to multipart/form-data:
<form method="post" enctype="multipart/form-data">
...
Ref: https://www.learnrazorpages.com/razor-pages/forms/file-upload
Related
at this moment I am uploading any file and saving it. What I want is to generate an error message if the user does not select any file and press the upload button, but at this moment the only thing it does is redirect to another view whether or not it has selected files. I would like to know if there is another better way to generate the upload of these files
this is my controller
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
[HttpPost]
public void Upload(HttpPostedFileBase file)
{
string file = (file.FileName).ToLower();
try
{
file.SaveAs(Server.MapPath("~/Uploads/" + file));
}
catch (Exception e)
{
ViewBag.UploadError = "Upload file error";
}
}
}
this is the view:
#{
ViewBag.Title = "Home";
}
#using (Html.BeginForm("Transformation", "Xml", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="col-md-12 offset-md-5">
<div class="custom-file col col-lg-2">
<input type="file" name="file" class="custom-file-input" id="inputGroupFile01" aria-describedby="inputGroupFileAddon01">
<label class="custom-file-label" for="inputGroupFile01">Choose file</label>
</div>
<div class="col-5">
<button class="btn btn-success col-md-4 mt-2" type="submit">Upload file</button>
</div>
</div>
//Message Error
<div class="alert alert-danger" role="alert">
<p class="text-danger">
#ViewBag.UploadError
</p>
</div>
}
#Thomas Caycedo Martinez, I believe you can simply modify your controller method like below.
If error, return to the same view with an error message.
Your view remains unchanged.
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
if (file != null)
{
string fileName = (file.FileName).ToLower();
try
{
file.SaveAs(Server.MapPath("~/Uploads/" + fileName));
}
catch (Exception e)
{
ViewBag.UploadError = "Upload file error";
return View("Index");
}
}
else {
ViewBag.UploadError = "Upload file error";
return View("Index");
}
return View();
}
write the action and controller correctly
#using (Html.BeginForm("Upload", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
and check file in action
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
if( file != null && file.Length > 0)
{
string file = (file.FileName).ToLower();
try
{
file.SaveAs(Server.MapPath("~/Uploads/" + file));
}
catch (Exception e)
{
ViewBag.UploadError = "Upload file error";
}
return View("Index");
}
else
{
//do something
return View("Index");
}
}
You can use JavaScript/JQuery to check for the same..
$(function(){
$("#btnSubmit").on("click",function(){
if($("#inputGroupFile01").val()==""){
alert("Please select a file")
return false;
}
})
})
where btnSubmit is the id for the submit button
am having a weird functionality when am only updating other fields other than the image , the other fields (FirstName , LastName) update successfully but then the Image sets its self to null, but when I choose it with respect to other fields (FirstName, LastName) it updates successfully. So what I want is when I don't update the image it stays as it is without setting its self to null.
This is my New.cshtm file which handles both Creating and Editing data :
<form asp-action="New" method="Post" asp-controller="Student" enctype="multipart/form-data">
<div asp-validation-summary="All"></div>
<input asp-for="Id" type="hidden"/>
<input name="IsEditMode" id="IsEditMode" value="#ViewBag.IsEditMode" type="hidden"/>
<div class="form-row">
<label>Upload Photo</label>
<input asp-for="ImageUrl" type="file" id="file" name="file" class="form-control"/>
</div>
<div class="form-row">
<div class="col">
<label asp-for="FirstName"></label>
<input asp-for="FirstName" class="form-control"/>
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
<div class="col">
<label asp-for="MiddleName"></label>
<input asp-for="MiddleName" class="form-control"/>
<span asp-validation-for="MiddleName" class="text-danger"></span>
</div>
</div>
</form>
Then these are the methods of my controller Student.cs am using to update the fields :
public IActionResult New(Student student, string IsEditMode, IFormFile file)
{
if (!ModelState.IsValid)
{
ViewBag.IsEditMode = IsEditMode;
return View(student);
}
try
{
if (IsEditMode.Equals("false"))
{
_studentRepository.Create(student);
UploadFile(file, student.Id);
_toastNotification.AddSuccessToastMessage("Student has been created successfully.");
}
else
{
_studentRepository.Edit(student);
UploadFile(file, student.Id);
_toastNotification.AddSuccessToastMessage("Student has been edited successfully.");
}
return RedirectToAction(nameof(Index));
}
catch (Exception e)
{
return RedirectToAction(nameof(Index));
}
}
public IActionResult Edit(int id)
{
try
{
ViewBag.IsEditMode = "true";
var student = _studentRepository.GetSingleStudent(id);
return View("New", student);
}
catch (Exception ex)
{
return Content("Could not find Pet");
}
}
public void UploadFile(IFormFile file, long studentId)
{
var fileName = file.FileName;
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/images",fileName);
using (var fileStream = new FileStream(path, FileMode.Create))
{
file.CopyTo(fileStream);
}
var student = _studentRepository.GetSingleStudent(studentId);
student.ImageUrl = fileName;
_studentRepository.Edit(student);
}
Then, this is how I am Updating in the Repository :
Then in my Repository, this is how I am updating the fields " :
public void Edit(Student student)
{
var existingStudent = _context.Students
.FirstOrDefault(s => s.Id == student.Id);
if (existingStudent != null)
{
// updating student.
_context.Entry(existingStudent).CurrentValues.SetValues(student);
_context.Entry(existingStudent).State = EntityState.Modified;
}
}
The Index.cshtml, this is one list the images and the First and LastName and the (action buttons) Edit, Delete buttons :
<table class="table table-striped">
<thead class="thead-dark">
<tr>
<td ><b>Student Picture</b></td>
<td><b>FirstName</b></td>
<td><b>LastName</b></td>
<td colspan="2"> <b>Actions</b></td>
</tr>
</thead>
<tbody>
#foreach (var student in Model)
{
<tr>
<td>#student.StudentRegNo</td>
<td>
<div class="thumbnail">
<img src="/images/#student.ImageUrl" width="90" height="90"/>
</div>
</td>
<td>#student.FirstName</td>
<td>#student.LastName</td>
<d>
<td>
<a class="btn btn-warning" asp-action="Details" asp-controller="Student" asp-route-Id="#student.Id">Details</a>
</td>
<td>
<a class="btn btn-primary" asp-action="edit" asp-route-Id="#student.Id">Edit</a>
</td>
<td>
<a
class="btn btn-danger delete"
asp-route-Id="#student.Id"
asp-action="Delete">
Delete
</a>
</td>
</d>
</tr>
}
</tbody>
</table>
EDIT
This is the current logic am having according to #Raul solution, but it's not working :
if (student.ImageUrl != null)
{
_context.Entry(existingStudent).CurrentValues.SetValues(student);
_context.Entry(existingStudent).State = EntityState.Modified;
}
else
{
_context.Entry(existingStudent).Property(x => x.ImageUrl).IsModified = false;
_context.Entry(existingStudent).CurrentValues.SetValues(student);
_context.Entry(existingStudent).State = EntityState.Modified;
}
You could consider the two scenario separately.When updating fields without updating the image, the file is null, then you need to assgin the ImageUrl of existing student to the posted student.
//...
try
{
if (IsEditMode.Equals("false"))
{
_studentRepository.Create(student);
UploadFile(file, student.Id);
_toastNotification.AddSuccessToastMessage("Student has been created successfully.");
}
else
{
//edit mode
if(file == null)//Updating fields without updating the image.
{
var existingStudent = _context.Students.FirstOrDefault(s => s.Id == student.Id);
if (existingStudent != null)
{
// updating student with previousImageUrl
student.ImageUrl = existingStudent.ImageUrl;
_context.Entry(existingStudent).CurrentValues.SetValues(student);
_context.Entry(existingStudent).State = EntityState.Modified;
_context.SaveChanges();
}
}
else//Updating the fields and the image
{
_studentRepository.Edit(student);
UploadFile(file, student.Id);
}
_toastNotification.AddSuccessToastMessage("Student has been edited successfully.");
}
return RedirectToAction(nameof(Index));
}
catch (Exception e)
{
return RedirectToAction(nameof(Index));
}
}
public void UploadFile(IFormFile file, long studentId)
{
var fileName = file.FileName;
var path = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/images", fileName);
using (var fileStream = new FileStream(path, FileMode.Create))
{
file.CopyTo(fileStream);
}
var student = _studentRepository.GetSingleStudent(studentId);
student.ImageUrl = fileName;
_studentRepository.Edit(student);
}
public void Edit(Student student)
{
var existingStudent = _context.Students
.FirstOrDefault(s => s.Id == student.Id);
if (existingStudent != null)
{
// updating student.
_context.Entry(existingStudent).CurrentValues.SetValues(student);
_context.Entry(existingStudent).State = EntityState.Modified;
_context.SaveChanges();
}
}
In your Edit student method, you can set the IsModified to false for your ImageUrl Property which will not update the image field in your database:
public void Edit(Student student)
{
var existingStudent = _context.Students.FirstOrDefault(s => s.Id == student.Id);
if (existingStudent != null)
{
// updating student.
_context.Student.Attach(existingStudent);
_context.Entry(existingStudent).State = EntityState.Modified;
_context.Entry(existingStudent).Property(x => x.ImageUrl).IsModified=false;
_context.SaveChanges();
}
}
Of course, you would need to check for your ImageUrl logic in this. If you get a new image then you update your model accordingly.
EDIT:
You can incorporate your if-else condidition like this:
if (student.ImageUrl != null)
{
_context.Student.Add(existingStudent);
_context.Entry(existingStudent).State EntityState.Modified;
//_context.Student.Update(existingStudent); //You can also use this. Comment out the upper two lines
_context.SaveChanges();
}
else
{
// updating student.
_context.Student.Attach(existingStudent);
_context.Entry(existingStudent).State = EntityState.Modified;
_context.Entry(existingStudent).Property(x => x.ImageUrl).IsModified=false;
_context.SaveChanges();
}
I am attempting to upload a file. The code below works on my local machine or running on a remote server running the dll from a command line, but when I try and publish to my test environment and run under iis it fails.
<form method="post" asp-action="Upload" asp-controller="Prebook" enctype="multipart/form-data">
<div class="form-inline">
<div class="col-md-2">
<div class="form-group">
<input type="file" name="files" data-input= "false" multiple class="filestyle" data-buttonName="btn-primary">
</div>
</div>
<div class="col-md-2">
<div class="form-group">
<input type="submit" value="Upload File" class="btn btn-primary" />
</div>
</div>
</div>
</form>
Controller logic
[HttpPost]
public async Task<IActionResult> Upload(ICollection<IFormFile> files)
{
if (await _contextPB.UploadRow.AnyAsync())
{
Danger(string.Format("Please LOAD the existing containers before uploading another file"), true);
return View();
}
int rowCount = 0;
var uploads = Path.Combine(_environment.WebRootPath, "uploads");
var _viewModel = new UploadViewModel();
foreach (var file in files)
{
using (var streamReader = System.IO.File.OpenText(Path.Combine(uploads, file.FileName)))
{
var line = streamReader.ReadLine();
var columnNames = line.Split(new[] { ',' });
if (!ValidateColumnNames(columnNames))
{
Danger(string.Format("Invalid Column Name in Upload file"), true);
return View(_viewModel);
}
while (!streamReader.EndOfStream)
{
var data = line.Split(new[] { ',' });
var uploadRow = new UploadRow();
// validation & assignment logic removed
try
{
_contextPB.Add(uploadRow);
rowCount++;
}
catch (Exception e)
{
Danger(string.Format("<b>{0},{1}</b> database error", uploadRow.Container_Id, e), true);
}
line = streamReader.ReadLine();
}
}
}
}
Try adding a catch block to see what the error is.
I'm assuming a permission issue.
[HttpPost]
public async Task<IActionResult> Upload(ICollection<IFormFile> files)
{
try
{
if (await _contextPB.UploadRow.AnyAsync())
{
Danger(string.Format("Please LOAD the existing containers before uploading another file"), true);
return View();
}
// your code
}
catch (Exception ex)
{
// what is the error?
// add a breakpoint to see
throw;
}
}
This is my View
Test Upload File
<form action="#Url.Action("Index", "Home")" method="post" enctype="multipart/form-data">
#Html.AntiForgeryToken()
<label for="file">Filename:</label>
<input type="file" name="files" id="files" />
<input type="submit" name="submit" value="Upload" />
</form>
This is my Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(IEnumerable<HttpPostedFileBase> files)
{
if (files != null)
{
foreach (var file in files)
{
try
{
if (file != null && file.ContentLength > 0)
{
var fileName = file.FileName;
var path = Path.Combine(Server.MapPath(#"\Upload"), fileName);
file.SaveAs(path);
ViewBag.Message = "File uploaded successfully";
}
}
catch (Exception ex)
{
ViewBag.Message = "ERROR:" + ex.Message.ToString();
}
}
}
return View();
}
The problem is the HttpPostedFileBase files is always null. I cant find the problem.
Here is an example how to use form onsubmit method
Your HTML part
<form id="formTest" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="files" id="files" />
<input type="submit" name="submit" value="Upload" />
</form>
script
<script type="text/javascript">
var form = document.getElementById('formTest').onsubmit = function (e) {
e.preventDefault();
var formdata = new FormData(); //FormData object
var fileInput = document.getElementById('files');
if (fileInput != "" && fileInput.files.length > 0) {
//Iterating through each files selected in fileInput
for (i = 0; i < fileInput.files.length; i++) {
//Appending each file to FormData object
formdata.append(fileInput.files[i].name, fileInput.files[i]);
}
//Creating an XMLHttpRequest and sending
var xhr = new XMLHttpRequest();
var url = '#Url.Action("Index","Home")';
xhr.open('POST', url);
xhr.send(formdata);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = xhr.responseText;
}
}
return false;
}
}
</script>
C#
public ActionResult Index()
{
if (Request.Files.Count > 0)
{
var file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Images/"), fileName);
file.SaveAs(path);
}
return View();
}
}
You can also handle files with Request.Files like this:
public ActionResult Index()
{
if (Request.Files.Count > 0)
{
var file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/Images/"), fileName);
file.SaveAs(path);
}
}
}
And for your second question, please try to use it between Html.BeginForm instead of form like this:
#using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<label>Filename:</label>
<input type="file" name="file1"/>
<input type="submit" name="submit" value="Upload" />
}
This is a part of my view code for Index action of Manage Controller.
<div class="mngimg">
#using (Html.BeginForm("UploadPhoto", "Manage", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="btn btn-default browseimg">
<input type="file" name="file" id="files" onchange="this.form.submit()" />
</div>
<div class="btn btn-default browseimg">
#Html.ActionLink("Remove Photo", "RemovePhoto", "Manage")
</div>
}
</div>
</div>
}
</dd>
<dt>Password:</dt>
<dd>
[
#if (Model.HasPassword) <!-- Here is my error. The Model is null -->
{
#Html.ActionLink("Change your password", "ChangePassword")
}
else
{
#Html.ActionLink("Create", "SetPassword")
}
]
</dd>
Whenever I open this page and click "Remove Photo" I keep getting an error saying that An exception of type 'System.NullReferenceException' occurred in App_Web_ckoryptg.dll but was not handled in user code. I tried debugging, but I am unable to figure out why my Model.HasPassword is becoming null. Here is my RemovePhoto Action from Manage Controller.
[HttpPost]
public async Task<ActionResult> UploadPhoto(HttpPostedFileBase file)
{
if (file != null && file.ContentLength > 0)
{
var user = await GetCurrentUserAsync();
var userId = user.Id;
var fileExt = Path.GetExtension(file.FileName);
var fnm = userId + ".png";
if (fileExt.ToLower().EndsWith(".png") || fileExt.ToLower().EndsWith(".jpg") || fileExt.ToLower().EndsWith(".gif"))// Important for security if saving in webroot
{
var filePath = HostingEnvironment.MapPath("~/Content/Images/") + fnm;
var directory = new DirectoryInfo(HostingEnvironment.MapPath("~/Content/Images/"));
if (directory.Exists == false)
{
directory.Create();
}
ViewBag.FilePath = filePath.ToString();
file.SaveAs(filePath);
return RedirectToAction("Index", new { Message = ManageMessageId.PhotoUploadSuccess });
}
else
{
return RedirectToAction("Index", new { Message = ManageMessageId.FileExtensionError });
}
}
return RedirectToAction("Index", new { Message = ManageMessageId.Error });// PRG
}
private async Task<ApplicationUser> GetCurrentUserAsync()
{
return await UserManager.FindByIdAsync(User.Identity.GetUserId());
}
I opened a default MVC project that comes with visual studio and I added these extra things that I followed from this tutorial ASP.NET upload images. How do I resolve this?
Edit:
This is my RemovePhoto action.
public ActionResult RemovePhoto()
{
string file = "~/Content/Images/" + User.Identity.GetUserId() + ".png";
if(System.IO.File.Exists(Server.MapPath(file)))
System.IO.File.Delete(Server.MapPath(file));
return View("Index");
}
Just Redirect back to your Index action. That way you don't have to instantiate your Index model in your RemovePhoto action. Can read more about this pattern here.