During this session, we will discuss Upload File in ASP.NET Core and Delete File in ASP.NET Core and implement these features on our Web App. Also, we use the GUID class to assign a unique file name for the uploaded file. Then, we are going to store the file name inside the respective field for further use.
GUID stands for Globally Unique Identifier and is a 128 bits string that assigns as Identification. Developers use this feature for IDs on computers, networks, and Databases. GUID string looks like a serial number and is used for preventing duplication.
What is GUID?
GUID stands for Globally Unique Identifier and is a 128 bits string that assigns as Identification. Developers use this feature for IDs on computers, networks, and Databases.
Upload File in ASP.NET Core
To upload file(s) in ASP.NET Core we need to pass the file from View to Controller. Thus, we need to add the IFormFile property to the respective Model. Moreover, we need to set the enctype attribute of the form tag to multipart/form-data. Later, we should add the respective input element to the form which should be bound to the IFormFile property with aspfor tag helper.
For better understanding, we implement the mentioned changes on our Web App and start with adding the IFormFile property to CreateCostViewModel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class CreateCostViewModel { [Column(TypeName = "decimal(18, 2)")] [Required] [DataType(DataType.Currency)] public decimal Amount { get; set; } [Required] [DataType(DataType.Date)] [Display(Name = "Registered Data")] public DateTime RegisteredDate { get; set; } [MaxLength(150)] public string Comment { get; set; } [Required] [Display(Name = "Category")] [RegularExpression("^\\d+$", ErrorMessage ="Please select a category")] public int CategoryID { get; set; } [Display(Name = "Payment Method")] [Required(ErrorMessage ="This option is mandetory, please select a payment method")] public PaymentMethods? PaymentMethod { set; get; } public IFormFile UploadFile { get; set; } } |
Next, we implement the required changes in Create View.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
@model CreateCostViewModel @{ ViewBag.Title = "Create Cost"; } <form enctype="multipart/form-data" asp-controller="Cost" asp-action="Create" method="post" class="mt-5 offset-3 col-6 border border-primary pt-2 pb-2 px-2 py-2"> <div> <label asp-for="Amount" class="form-label"></label> <input asp-for="Amount" class="form-control" /> <span asp-validation-for="Amount" class="text-danger"></span> </div> <div> <label asp-for="RegisteredDate" class="form-label"></label> <input asp-for="RegisteredDate" class="form-control" /> <span asp-validation-for="RegisteredDate" class="text-danger"></span> </div> <div> <label asp-for="Comment" class="form-label"></label> <input asp-for="Comment" class="form-control" /> <span asp-validation-for="Comment" class="text-danger"></span> </div> <div> <label asp-for="CategoryID" class="form-label"></label> <select asp-for=CategoryID asp-items="@ViewBag.Categories" class="form-select"></select> <span asp-validation-for="CategoryID" class="text-danger"></span> </div> <div> <label asp-for="PaymentMethod" class="form-label"></label> <select asp-for="PaymentMethod" class="form-select" asp-items="@ViewBag.PaymentMethods"></select> <span asp-validation-for="PaymentMethod" class="text-danger"></span> </div> <div> <label asp-for="UploadFile" class="form-label"></label> <input asp-for="UploadFile" class="form-control" /> </div> <div><button type="submit" class="btn btn-primary mt-2">Create</button></div> </form> |
Now, we need to do some changes to the respective Controller. First, to have access to the path of the Web App, we can inject an object of IWebHostEnvironment through the Controller Constructor. Next, we need to check whether posted data has a file or not. If there is a file, we should assign a unique file name to the uploaded file via GUID class. Moreover, we need to find the destination of the file through the object of IWebHostEnvironment. After that, we make a FileStream and copy the file to the destination with the CopyTo method of the IFormFile object.
To apply the above changes to our Web App, first, we inject IWebHostEnvironment into the CostController.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
private readonly ICostRepository costRepository; private readonly ICategoryRepository categoryRepository; private readonly IWebHostEnvironment webHostEnvironment; public CostController(ICostRepository _costRepository, ICategoryRepository _categoryRepository, IWebHostEnvironment _webHostEnvironment) { costRepository = _costRepository; categoryRepository = _categoryRepository; webHostEnvironment = _webHostEnvironment; } |
Next, to obey the Don’t Repeat Yourself principle, we create a Non-Action Method for the upload process. This method handles assigning unique file name processes as well.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private string UploadFile(IFormFile formFile) { string UniqueFileName = Guid.NewGuid().ToString() + "-" + formFile.FileName; string TargetPath = Path.Combine(webHostEnvironment.WebRootPath, "images", UniqueFileName); using (var stream = new FileStream(TargetPath, FileMode.Create)) { formFile.CopyTo(stream); } return UniqueFileName; } |
Finally, inside Create Action Method with HttpPost decoration, we check the existence of the file and call the UploadFile method in case of file existence. Also, we store the unique file name inside the InvoiceImagePath property of the Cost entity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
[HttpPost] public IActionResult Create(CreateCostViewModel model) { if (ModelState.IsValid) { Cost cost = new() { Amount = model.Amount, Comment = model.Comment, RegisteredDate = model.RegisteredDate, CategoryID = model.CategoryID, PaymentMethod = model.PaymentMethod }; if(model.UploadFile != null) { cost.InvoiceImagePath = UploadFile(model.UploadFile); } costRepository.Create(cost); return RedirectToAction("Index"); } LoadDropdownList(); return View(model); } |
Delete File in ASP.NET Core
To delete an uploaded file, just we need to find its path. Then we can manage to delete files with System.IO.File.Delete method.
Here, we need to implement the Delete File process for the Update and Delete Action Method of CostController. Hence, for the Update operation, we need to create a ViewModel that contains the IFormFile property. As most of the properties are the same between CreateCostViewModel and the ViewModel which we will create for Update purposes, the new ViewModel can be implemented from CreateCostViewModel.
1 2 3 4 5 6 7 8 9 |
public class UpdateCostViewModel : CreateCostViewModel { public int ID { get; set; } public string ExsitingFile { get; set; } } |
Then, we should modify Update View to display the possible record image and input element to update the file of the record.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
@model UpdateCostViewModel @{ ViewBag.Title = "Create Cost"; var imagepath = "~/images/" + (Model.ExsitingFile ?? "no-image.png"); } <form asp-controller="Cost" enctype="multipart/form-data" asp-action="Update" method="post" class="mt-5 offset-3 col-6 border border-primary pt-2 pb-2 px-2 py-2"> <input hidden asp-for="ID" /> <div> <label asp-for="Amount" class="form-label"></label> <input asp-for="Amount" class="form-control" /> <span asp-validation-for="Amount" class="text-danger"></span> </div> <div> <label asp-for="RegisteredDate" class="form-label"></label> <input asp-for="RegisteredDate" class="form-control" /> <span asp-validation-for="RegisteredDate" class="text-danger"></span> </div> <div> <label asp-for="Comment" class="form-label"></label> <input asp-for="Comment" class="form-control" /> <span asp-validation-for="Comment" class="text-danger"></span> </div> <div> <label asp-for="CategoryID" class="form-label"></label> <select asp-for=CategoryID asp-items="@ViewBag.Categories" class="form-select"></select> <span asp-validation-for="CategoryID" class="text-danger"></span> </div> <div> <label asp-for="PaymentMethod" class="form-label"></label> <select asp-for="PaymentMethod" class="form-select" asp-items="@ViewBag.PaymentMethods"></select> <span asp-validation-for="PaymentMethod" class="text-danger"></span> </div> <div> <label asp-for="UploadFile" class="form-label"></label> <input asp-for="UploadFile" class="form-control" /> </div> <img src="@imagepath" asp-append-version="true" class="img-thumbnail"/> <div class="row mt-2"> <div class="col-3 d-grid"> <button type="submit" class="btn btn-primary">Edit</button> </div> <div class="col-3 d-grid"> <a asp-action="index" class="btn btn-info">Cost List</a> </div> </div> </form> |
As you can see in the above code, we create an object to have access to an image of the record to be displayed during the update process. Also, we implement the same technique for the Detail view to display the image of the selected record. you can review the Detail view code below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
@model Cost @{ ViewBag.Title = "Cost Detail"; var imagepath = "~/images/" + (Model.InvoiceImagePath ?? "no-image.png"); } <div class="row"> <div class="col-lg-4 offset-lg-4 col-md-6 offset-md-3 col-12"> <div class="card"> <img src="@imagepath" class="card-img-top" asp-append-version="true"/> <div class="card-header"> Detail Cost ID @Model.ID </div> <div class="card-body"> <p class="card-text">Amount: @Model.Amount</p> <p class="card-text">Category: @Model.CategoryID</p> <p class="card-text">Comment: @Model.Comment</p> <p class="card-text">Payment Method: @Model.PaymentMethod</p> <a class="btn btn-primary" asp-controller="cost" asp-action="index">Cost List</a> </div> </div> </div> </div> |
As we changed the Model of the Update View, we should do some changes inside Update Action Method with HttpGet decoration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[HttpGet] public IActionResult Update(int id) { var cost = costRepository.GetCostByID(id); UpdateCostViewModel model = new() { ID = cost.ID, Amount = cost.Amount, RegisteredDate = cost.RegisteredDate, Comment = cost.Comment, CategoryID = cost.CategoryID, PaymentMethod = cost.PaymentMethod, ExsitingFile = cost.InvoiceImagePath }; LoadDropdownList(); return View(model); } |
Then we need to check whether posted data to the Update Action Method contains a file or not. If there is a file, we need to replace it with the possible former image. Thus, we check if the record has uploaded an image, first we delete the previous image and then upload the new one. Also, if the record does not have a previous image, we just need to upload the current image.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
[HttpPost] public IActionResult Update(UpdateCostViewModel model) { if (ModelState.IsValid) { Cost UpdatedCost = costRepository.GetCostByID(model.ID); UpdatedCost.Amount = model.Amount; UpdatedCost.RegisteredDate = model.RegisteredDate; UpdatedCost.Comment = model.Comment; UpdatedCost.CategoryID = model.CategoryID; UpdatedCost.PaymentMethod = model.PaymentMethod; if(model.UploadFile != null) { if(UpdatedCost.InvoiceImagePath != null) { string ExitingFile = Path.Combine(webHostEnvironment.WebRootPath, "images", UpdatedCost.InvoiceImagePath); System.IO.File.Delete(ExitingFile); } UpdatedCost.InvoiceImagePath = UploadFile(model.UploadFile); } costRepository.Update(UpdatedCost); return RedirectToAction("Index"); } LoadDropdownList(); return View(model); } |
The last part of this session is implementing the Delete process in Delete Action Method. Hence, we check the record that is posted to the Delete Action method, and if there is a file, we delete the file first and then delete the record from the Database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[HttpPost] public IActionResult Delete(int id) { Cost cost = costRepository.GetCostByID(id); if (cost.InvoiceImagePath != null) { string ExitingFile = Path.Combine(webHostEnvironment.WebRootPath, "images", cost.InvoiceImagePath); System.IO.File.Delete(ExitingFile); } costRepository.Delete(id); return RedirectToAction("index"); } |
If you need more details, watch this session video. Also, for being updated about our coming sessions, follow us on Instagram, Facebook, Telegram, or YouTube. Moreover, you can have access to the list of all sessions HERE and you can download this session source code from our GitHub.
You can download this Session Slides form HERE.