How to Upload File in ASP.NET Core with ViewModel?
In ASP.NET Core it's very easy to upload a file, but a one need to know about interface IFormFile. So once you know about it all done. But let's have a look, how we can use it.
Note: Don't rely on the FileName property of IFormFile without validation. The FileName property should only be used for display purposes.
So to use this IFormFile we first need to create a ViewModel, which will have the property of IFormFile. To keep it easy and small I am using small ViewModel as Item. So my Item class looks like,
using System;
using System.Linq;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
namespace FileUploader.Models {
public class Item {
public int ItemID {get;set;}
[Required(ErrorMessage = "Please Type Item Name")]
[Display(Name = "Item Name")]
public string ItemName {get; set;}
[Required(ErrorMessage = "Please Upload File")]
[Display(Name = "Upload File")]
[ValidateImage] //This is custom Attribute class, Can be removed
public IFormFile formFile {get;set;}
}
}
So my ValidateImage Attribute Class looks like,
using System;
using System.Linq;
using Microsoft.AspNetCore.Http;
using System.ComponentModel.DataAnnotations;
namespace FileUploader.Models {
public class ValidateImageAttribute: ValidationAttribute {
public override bool IsValid(object value) {
int MaxContentLength = 1024 * 1024 * 2; //Max 2 MB file
string[] AllowedFileExtensions = new
string[] {
".jpg",
".gif",
".png"
};
var file = value as IFormFile;
if (file == null)
return false;
else if (!AllowedFileExtensions.Contains((file != null) ?
file.FileName.Substring(file.FileName.LastIndexOf('.')).ToLower() :
string.Empty)) {
ErrorMessage = "Please upload Your Photo of type: " +
string.Join(", ", AllowedFileExtensions);
return false;
} else if (file.Length > MaxContentLength) {
ErrorMessage = "Your Photo is too large, maximum allowed size is : " +
(MaxContentLength / 1024).ToString() + "MB";
return false;
} else
return true;
}
}
}
Now Let's Create our File Upload Helper Class, so that a logic can be reused in other case as well, so I just created a Class FileUploadHelper, which looks like,
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace FileUploader.Models {
public class FileUploadHelper {
public async Task < string > SaveFileAsync(IFormFile file, string pathToUplaod) {
string imageUrl = string.Empty;
if (!Directory.Exists(pathToUplaod))
System.IO.Directory.CreateDirectory(pathToUplaod); //Create Path of not exists
string pathwithfileName = pathToUplaod + "\\" + GetFileName(file, true);
using(var fileStream = new FileStream(pathwithfileName, FileMode.Create)) {
await file.CopyToAsync(fileStream);
}
imageUrl = pathwithfileName;
return imageUrl;
}
public string SaveFile(IFormFile file, string pathToUplaod) {
string imageUrl = string.Empty;
string pathwithfileName = pathToUplaod + "\\" + GetFileName(file, true);
using(var fileStream = new FileStream(pathwithfileName, FileMode.Create)) {
file.CopyTo(fileStream);
}
return imageUrl;
}
public string GetFileName(IFormFile file, bool BuildUniqeName) {
string fileName = string.Empty;
string strFileName = file.FileName.Substring(
file.FileName.LastIndexOf("\\"))
.Replace("\\", string.Empty);
string fileExtension = GetFileExtension(file);
if (BuildUniqeName) {
string strUniqName = GetUniqueName("img");
fileName = strUniqName + fileExtension;
} else {
fileName = strFileName;
}
return fileName;
}
public string GetUniqueName(string preFix) {
string uName = preFix + DateTime.Now.ToString()
.Replace("/", "-")
.Replace(":", "-")
.Replace(" ", string.Empty)
.Replace("PM", string.Empty)
.Replace("AM", string.Empty);
return uName;
}
public string GetFileExtension(IFormFile file) {
string fileExtension;
fileExtension = (file != null) ?
file.FileName.Substring(file.FileName.LastIndexOf('.')).ToLower() :
string.Empty;
return fileExtension;
}
}
}
The name of the methods are self explainable, Now we need to create Controller to Call this and render our View, So my Controller looks like,
public IActionResult AddItem() {
return View();
}
[HttpPost]
public async Task < IActionResult > AddItem(Item obItem) {
var uploads = Path.Combine(_hostingEnvironment.WebRootPath, "uploads");
FileUploadHelper objFile = new FileUploadHelper();
string strFilePath = await objFile.SaveFileAsync(obItem.formFile, uploads);
strFilePath = strFilePath
.Replace(_hostingEnvironment.WebRootPath, string.Empty)
.Replace("\\", "/"); //Relative Path can be stored in database or do logically what is needed.
return View();
}
Now we need to have a View, So my View looks like,
@model FileUploader.Models.Item
@{
ViewData["Title"] = "AddItem";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>AddItem</h2>
<h4>Item</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="AddItem" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="ItemID" class="control-label"></label>
<input asp-for="ItemID" class="form-control" />
<span asp-validation-for="ItemID" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ItemName" class="control-label"></label>
<input asp-for="ItemName" class="form-control" />
<span asp-validation-for="ItemName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="formFile" class="control-label"></label>
<input asp-for="formFile" type="file" class="form-control" />
<span asp-validation-for="formFile" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
In the above view you have to look for the file upload that is,
<div class="form-group">
<label asp-for="formFile" class="control-label"></label>
<input asp-for="formFile" type="file" class="form-control" />
<span asp-validation-for="formFile" class="text-danger"></span>
</div>
And one more a form attribute,
<form asp-action="AddItem" enctype="multipart/form-data">
A enctype="multipart/form-data" is must for file upload.
So all set, now if we run our application, we can see our view looks like,
So hopefully this will help if you need to work with this ViewModel way else if you using JQuery to upload then just post the form with your best way how doing and use the controller call code in your service area or as it needed.