Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate uploaded file in ASP.Net Core

I'm using ASP.NET Core 2.2 and I'm using model binding for uploading file.

This is my UserViewModel

public class UserViewModel {     [Required(ErrorMessage = "Please select a file.")]     [DataType(DataType.Upload)]     public IFormFile Photo { get; set; } } 

This is MyView

@model UserViewModel  <form method="post"       asp-action="UploadPhoto"       asp-controller="TestFileUpload"       enctype="multipart/form-data">     <div asp-validation-summary="ModelOnly" class="text-danger"></div>      <input asp-for="Photo" />     <span asp-validation-for="Photo" class="text-danger"></span>     <input type="submit" value="Upload"/> </form> 

And finally this is MyController

[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> UploadPhoto(UserViewModel userViewModel) {     if (ModelState.IsValid)     {         var formFile = userViewModel.Photo;         if (formFile == null || formFile.Length == 0)         {             ModelState.AddModelError("", "Uploaded file is empty or null.");             return View(viewName: "Index");         }          var uploadsRootFolder = Path.Combine(_environment.WebRootPath, "uploads");         if (!Directory.Exists(uploadsRootFolder))         {             Directory.CreateDirectory(uploadsRootFolder);         }          var filePath = Path.Combine(uploadsRootFolder, formFile.FileName);         using (var fileStream = new FileStream(filePath, FileMode.Create))         {             await formFile.CopyToAsync(fileStream).ConfigureAwait(false);         }          RedirectToAction("Index");     }     return View(viewName: "Index"); } 

How can I limit uploaded files to lower than 5MB with specific extensions like .jpeg and .png ? I think both of these validations are done in the ViewModel. But I don't know how to do that.

like image 753
Arian Shahalami Avatar asked Jun 13 '19 21:06

Arian Shahalami


2 Answers

You could custom validation attribute MaxFileSizeAttribute like below

MaxFileSizeAttribute

public class MaxFileSizeAttribute : ValidationAttribute {     private readonly int _maxFileSize;     public MaxFileSizeAttribute(int maxFileSize)     {         _maxFileSize = maxFileSize;     }      protected override ValidationResult IsValid(     object value, ValidationContext validationContext)     {         var file = value as IFormFile;         if (file != null)         {            if (file.Length > _maxFileSize)             {                 return new ValidationResult(GetErrorMessage());             }         }          return ValidationResult.Success;     }      public string GetErrorMessage()     {         return $"Maximum allowed file size is { _maxFileSize} bytes.";     } } 

AllowedExtensionsAttribute

public class AllowedExtensionsAttribute : ValidationAttribute {     private readonly string[] _extensions;     public AllowedExtensionsAttribute(string[] extensions)     {         _extensions = extensions;     }          protected override ValidationResult IsValid(     object value, ValidationContext validationContext)     {         var file = value as IFormFile;         if (file != null)         {             var extension = Path.GetExtension(file.FileName);             if (!_extensions.Contains(extension.ToLower()))             {                 return new ValidationResult(GetErrorMessage());             }         }                  return ValidationResult.Success;     }      public string GetErrorMessage()     {         return $"This photo extension is not allowed!";     } } 

Add MaxFileSize attribute and AllowedExtensions attribute to Photo property

public class UserViewModel {         [Required(ErrorMessage = "Please select a file.")]         [DataType(DataType.Upload)]         [MaxFileSize(5* 1024 * 1024)]         [AllowedExtensions(new string[] { ".jpg", ".png" })]         public IFormFile Photo { get; set; }  } 
like image 168
Xueli Chen Avatar answered Sep 21 '22 01:09

Xueli Chen


This is how I use the custom validation attributes, almost the same as @xueli-chen 's answer but production-ready.

FileExtensionsAttribute

using System; using System.ComponentModel.DataAnnotations; using System.Globalization; using System.IO; using System.Linq;  using Microsoft.AspNetCore.Http;  using NewsPassWebApi.Properties;  namespace NewsPassWebApi.Models.DataAnnotaions {     /// <summary>     /// Validation attribute to assert an <see cref="IFormFile">IFormFile</see> property, field or parameter has a specific extention.     /// </summary>     [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]     public sealed class FileExtensionsAttribute : ValidationAttribute     {         private string _extensions;          /// <summary>         /// Gets or sets the acceptable extensions of the file.         /// </summary>         public string Extensions         {             get             {                 // Default file extensions match those from jquery validate.                 return string.IsNullOrEmpty(_extensions) ? "png,jpg,jpeg,gif" : _extensions;             }             set             {                 _extensions = value;             }         }          private string ExtensionsNormalized         {             get             {                 return Extensions.Replace(" ", "", StringComparison.Ordinal).ToUpperInvariant();             }         }          /// <summary>         /// Parameterless constructor.         /// </summary>         public FileExtensionsAttribute() : base(() => Resources.FileExtensionsAttribute_ValidationError)         { }          /// <summary>         /// Override of <see cref="ValidationAttribute.IsValid(object)"/>         /// </summary>         /// <remarks>         /// This method returns <c>true</c> if the <paramref name="value"/> is null.           /// It is assumed the <see cref="RequiredAttribute"/> is used if the value may not be null.         /// </remarks>         /// <param name="value">The value to test.</param>         /// <returns><c>true</c> if the value is null or it's extension is not invluded in the set extensionss</returns>         public override bool IsValid(object value)         {             // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null.             if (value == null)             {                 return true;             }              // We expect a cast exception if the passed value was not an IFormFile.             return ExtensionsNormalized.Split(",").Contains(Path.GetExtension(((IFormFile)value).FileName).ToUpperInvariant());         }          /// <summary>         /// Override of <see cref="ValidationAttribute.FormatErrorMessage"/>         /// </summary>         /// <param name="name">The name to include in the formatted string</param>         /// <returns>A localized string to describe the acceptable extensions</returns>         public override string FormatErrorMessage(string name)         {             return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, Extensions);         }     } } 

FileSizeAttribute

using System; using System.ComponentModel.DataAnnotations; using System.Globalization;  using Microsoft.AspNetCore.Http;  using NewsPassWebApi.Properties;  namespace NewsPassWebApi.Models.DataAnnotaions {     /// <summary>     /// Validation attribute to assert an <see cref="IFormFile">IFormFile</see> property, field or parameter does not exceed a maximum size.     /// </summary>     [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]     public sealed class FileSizeAttribute : ValidationAttribute     {         /// <summary>         /// Gets the maximum acceptable size of the file.         /// </summary>         public long MaximumSize { get; private set; }          /// <summary>         /// Gets or sets the minimum acceptable size of the file.         /// </summary>         public int MinimumSize { get; set; }          /// <summary>         /// Constructor that accepts the maximum size of the file.         /// </summary>         /// <param name="maximumSize">The maximum size, inclusive.  It may not be negative.</param>         public FileSizeAttribute(int maximumSize) : base(() => Resources.FileSizeAttribute_ValidationError)         {             MaximumSize = maximumSize;         }          /// <summary>         /// Override of <see cref="ValidationAttribute.IsValid(object)"/>         /// </summary>         /// <remarks>         /// This method returns <c>true</c> if the <paramref name="value"/> is null.           /// It is assumed the <see cref="RequiredAttribute"/> is used if the value may not be null.         /// </remarks>         /// <param name="value">The value to test.</param>         /// <returns><c>true</c> if the value is null or it's size is less than or equal to the set maximum size</returns>         /// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception>         public override bool IsValid(object value)         {             // Check the lengths for legality             EnsureLegalSizes();              // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null.             // We expect a cast exception if the passed value was not an IFormFile.             var length = value == null ? 0 : ((IFormFile)value).Length;              return value == null || (length >= MinimumSize && length <= MaximumSize);         }          /// <summary>         /// Override of <see cref="ValidationAttribute.FormatErrorMessage"/>         /// </summary>         /// <param name="name">The name to include in the formatted string</param>         /// <returns>A localized string to describe the maximum acceptable size</returns>         /// <exception cref="InvalidOperationException"> is thrown if the current attribute is ill-formed.</exception>         public override string FormatErrorMessage(string name)         {             EnsureLegalSizes();              string errorMessage = MinimumSize != 0 ? Resources.FileSizeAttribute_ValidationErrorIncludingMinimum : ErrorMessageString;              // it's ok to pass in the minLength even for the error message without a {2} param since String.Format will just ignore extra arguments             return string.Format(CultureInfo.CurrentCulture, errorMessage, name, MaximumSize, MinimumSize);         }          /// <summary>         /// Checks that MinimumSize and MaximumSize have legal values.  Throws InvalidOperationException if not.         /// </summary>         private void EnsureLegalSizes()         {             if (MaximumSize < 0)             {                 throw new InvalidOperationException(Resources.FileSizeAttribute_InvalidMaxSize);             }              if (MaximumSize < MinimumSize)             {                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.RangeAttribute_MinGreaterThanMax, MaximumSize, MinimumSize));             }         }     } } 

now you can use those like any built-in validation attribute including custom/localized error messages, max-min file sizes, and file extensions.

like image 42
Ahmed Kamal Avatar answered Sep 20 '22 01:09

Ahmed Kamal