我正在使用ASP.NET Core 2.2,并且正在使用模型绑定来上传文件。
这是我的 UserViewModel
public class UserViewModel
{
[Required(ErrorMessage = "Please select a file.")]
[DataType(DataType.Upload)]
public IFormFile Photo { get; set; }
}
这是 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>
最后是 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");
}
如何将上传的文件限制为5MB以下,扩展名为.jpeg和.png?我认为这两个验证都是在ViewModel中完成的。但是我不知道该怎么做。
答案 0 :(得分:1)
您可以像下面那样自定义验证属性MaxFileSizeAttribute
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;
//var extension = Path.GetExtension(file.FileName);
//var allowedExtensions = new[] { ".jpg", ".png" };`enter code here`
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;
var extension = Path.GetExtension(file.FileName);
if (!(file == null))
{
if (!_Extensions.Contains(extension.ToLower()))
{
return new ValidationResult(GetErrorMessage());
}
}
return ValidationResult.Success;
}
public string GetErrorMessage()
{
return $"This photo extension is not allowed!";
}
}
在MaxFileSize
属性中添加AllowedExtensions
属性和Photo
属性
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; }
}
答案 1 :(得分:1)
您可以IValidatableObject
验证模型。
public class UserViewModel : IValidatableObject
{
[Required(ErrorMessage = "Please select a file.")]
[DataType(DataType.Upload)]
public IFormFile Photo { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var photo = ((UserViewModel)validationContext.ObjectInstance).Photo;
var extension = Path.GetExtension(photo.FileName);
var size = photo.Length;
if (!extension.ToLower().Equals(".jpg"))
yield return new ValidationResult("File extension is not valid.");
if(size > (5 * 1024 * 1024))
yield return new ValidationResult("File size is bigger than 5MB.");
}
}
答案 2 :(得分:1)
这就是我使用自定义验证属性的方式,该属性几乎与@ xueli-chen的答案相同,但已可以投入生产。
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));
}
}
}
}
现在您可以使用任何类似内置验证属性的属性,包括自定义/本地化错误消息,最大最小文件大小和文件扩展名。
答案 3 :(得分:1)
在previous comment之后,您可以添加此类:
public class ValidateModelStateFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.ModelState.IsValid)
{
return;
}
var validationErrors = context.ModelState
.Keys
.SelectMany(k => context.ModelState[k].Errors)
.Select(e => e.ErrorMessage)
.ToArray();
var json = new JsonErrorResponse
{
Messages = validationErrors
};
context.Result = new BadRequestObjectResult(json);
}
}
最后,将过滤器添加到Startup类中的控制器:
services.AddControllers(options =>
{
options.Filters.Add(typeof(ValidateModelStateFilter));
})
答案 4 :(得分:0)
您可以按照下面的代码做
var formFile = userViewModel.Photo;
var extension = Path.GetExtension(formFile.FileName);
var allowedExtensions = new[] { ".jpeg", ".png" };
if (formFile == null || formFile.Length == 0)
{
ModelState.AddModelError("", "Uploaded file is empty or null.");
return View(viewName: "Index");
}
if (formFile != null && formFile.ContentLength > 0 &&
allowedExtensions.Contains(extension.ToLower())
&& formFile.ContentLength <= (5 * 1024 * 1024))
{
//do some logic here because the data is smaller than 5mb
}
else
{
// error
return View("Error");
}