How to call file upload from C# after user clicks save button
Hi so I have a file upload control made with ASP.NET Core MVC 5 and C#. So I need to upload a file and save the filepath to the SQL Server database via Entity Framework Core.
What I want is to call the UploadToFileSystem
method in the ProblemsController inside the Edit post action. I want to get rid of the button in the view with the same method name UploadToFileSystem
.
So that after user clicks the save button the method is called. So if I call UploadToFileSystem
on line 37 in Edit.cshtml the method works. It just doesn't save the file path to the database, obviously because I'm not calling it in the Edit
post action in the controller.
I will show you what it looks like if I call the method from the view in a UploadToFileSystem button just so I can show that my method is uploading files to the Files
folder even though most of my required
model
properties are null and return errors. Just to note I hosted the image on Imgur but can't see the link in my markdown source so if it doesn't work for you then here is the link https://imgur.com/9yVT9Mj
And now I want to show you what it looks like when I call the same method in the Edit method controller and comment out the button containing the same method. As you can see unlike before it is not returning null for the other properties as I am You will notice when I hit the breakpoint of the method signature that the List<IFormFile> files
parameter is not counting any files when I call that parameter in the files method. Here is the Imgur link to it if it doesn't work. WbXQwzt
Here is the code for the two methods I am using for ProblemsController.cs
csharp
[HttpPost]
public async Task<IActionResult> UploadToFileSystem(List<IFormFile> files, int? id, string title,
string description, DateTime dateTime, int severity)
{
foreach (var file in files)
{
//Get the base Path, i.e, The Current Directory of the application + /Files/.
var basePath = Path.Combine(Directory.GetCurrentDirectory() + "\\Files\\");
//Checks if the base path directory exists, else creates it.
bool basePathExists = System.IO.Directory.Exists(basePath);
if (!basePathExists) Directory.CreateDirectory(basePath);
//Gets the file name without the extension.
var fileName = Path.GetFileNameWithoutExtension(file.FileName);
//Combines the base path with the file name.
var filePath = Path.Combine(basePath, file.FileName);
//If the file doesnt exist in the generated path, we use a filestream object, and create a new file, and then copy the contents to it.
var extension = Path.GetExtension(file.FileName);
if (!System.IO.File.Exists(filePath))
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
//Create a new Problem object with required values.
var problem = await _context.Problems.FindAsync(id);
problem = new Problem
{
ProblemFileAttachments = filePath,
ProblemTitle = title,
ProblemDescription = description,
ProblemStartDate = dateTime,
ProblemSeverity = severity
};
//Inserts this model to the db via the context instance of EF Core.
_context.Problems.Add(problem);
_context.SaveChanges();
}
}
//Loads all the File data to an object and sets a message in the TempData.
TempData["Message"] = "File successfully uploaded to File System.";
return RedirectToAction("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,ProblemTitle,ProblemDescription,ProblemStartDate,
ProblemFileAttachments,ProblemSeverity,ProblemComplete")] Problem problem)
{
//Used for file attachment upload.
List<IFormFile> iFormFile = new List<IFormFile>();
if (id != problem.ID)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(problem);
await _context.SaveChangesAsync();
//Upload or update any attachments user inserted.
await UploadToFileSystem(iFormFile ,problem.ID, problem.ProblemTitle,
problem.ProblemDescription, problem.ProblemStartDate, problem.ProblemSeverity);
}
catch (DbUpdateConcurrencyException)
{
if (!ProblemExists(problem.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(problem);
}
Problems.cs (model)
csharp
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.DataAnnotations;
namespace Pitcher.Models
{
public class Problem
{
public int ID {get;set;}
[Required]
[StringLength(180, MinimumLength = 2, ErrorMessage = "Problem Title must be bettween 2 to 20 characters.")]
[DataType(DataType.Text)]
[Display(Name = "Problem Title")]
[Column("ProblemTitle")]
public string ProblemTitle {get;set;}
[Required]
[StringLength(int.MaxValue, MinimumLength = 5, ErrorMessage = "Problem Title must be at least 5 characters.")]
[DataType(DataType.Text)]
[Display(Name = "Problem Description")]
[Column("ProblemDescription")]
public string ProblemDescription {get;set;}
[Required]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = " Problem Start Date")]
[Column("ProblemStartDate")]
public DateTime ProblemStartDate {get;set;}
[DataType(DataType.Upload)]
[Display(Name = " Upload file")]
[Column("ProblemFileAttachments")]
public string ProblemFileAttachments {get;set;}
[Required]
[Display(Name = "Problem Severity")]
[Range(1,5, ErrorMessage
= "Problem Severity value for {0} must be between {1} and {2}.")]
[Column("ProblemSeverity")]
public int ProblemSeverity {get;set;}
[Display(Name = "Problem Complete")]
[Column("ProblemComplete")]
public bool ProblemComplete {get;set;}
public ICollection<Result> Result {get;set;}
public ICollection<Chat> Chat {get;set;}
//public List<Problem> ProblemFileModel {get; set;}
}
}
View file Edit.cshtml
html
@model Pitcher.Models.Problem
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Problem</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit" enctype="multipart/form-data" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ID" class="control-label" ></label>
<input asp-for="ID" readonly="true" disabled="true" class="form-control"/>
</div>
<div class="form-group">
<label asp-for="ProblemTitle" class="control-label"></label>
<input asp-for="ProblemTitle" class="form-control" />
<span asp-validation-for="ProblemTitle" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProblemDescription" class="control-label"></label>
<textarea asp-for="ProblemDescription" class="form-control" rows="10" cols="50"></textarea>
<span asp-validation-for="ProblemDescription" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProblemStartDate" class="control-label"></label>
<input asp-for="ProblemStartDate" class="form-control" />
<span asp-validation-for="ProblemStartDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ProblemFileAttachments" class="control-label"></label>
<input asp-for="ProblemFileAttachments" type="file" name="files"/>
@* <button type="submit" class="btn btn-primary" asp-controller="Problems" asp-action="UploadToFileSystem">Upload to File System</button> *@
</div>
<div class="form-group">
<label asp-for="ProblemSeverity" class="control-label"></label>
<select asp-for="ProblemSeverity" class="form-control">
<option value="">Choose severity level</option>
<option value="1">Very Low</option>
<option value="2">Low</option>
<option value="3">Medium</option>
<option value="4">High</option>
<option value="5">Very High</option>
</select>
<span asp-validation-for="ProblemSeverity" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="ProblemComplete" /> @Html.DisplayNameFor(model => model.ProblemComplete)
</label>
</div>
<div class="form-group" method="post">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}