According to the MSDN documentation, by default the FileExtensionsAttribute (.NET 4.5) should allow me to only upload only jpg,jpeg,gif and png files - which is what I want.
I tried uploading a jpg without the attribute, it works. Great. Then
I added the attribute to my view model..
[FileExtensions(ErrorMessage = "Please specify a valid image file (.jpg, .jpeg, .gif or .png)")]
public HttpPostedFileBase ImageFile { get; set; }
No joy. The verification fails and the ErrorMessage is shown. On top of that there doesn't seem to be a way to specify any allowed custom file extensions.
I ended up extending the FileExtensionsAttribute and using my own verification logic, which works as expected. But why doesn't this way work?
Will post the entire controller and view if required. I used this example as a basis for the uploading logic, but using the DataAnnotations.FileExtensionsAttribute instead of Microsoft.Web.Mvc.FileExtensions.. How do I upload images in ASP.NET MVC?
Use the Extensions property to set them. Although according to the documentation
The file name extensions, or the default file extensions (".png",
".jpg", ".jpeg", and ".gif") if the property is not set.
You can set it just like you did the ErrorMessage
. The more likely issue is that it doesn't know how to assess whether the HttpPostedFileBase
has the right extension. You'll need to use the one from the MVC framework or create your own.
Since System.ComponentModel.DataAnnotations.FileExtensionsAttribute is sealed. I use a wrapper for MVC 4.
public class HttpPostedFileExtensionsAttribute : DataTypeAttribute, IClientValidatable
{
private readonly FileExtensionsAttribute _innerAttribute =
new FileExtensionsAttribute();
/// <summary>
/// Initializes a new instance of the <see cref="HttpPostedFileExtensionsAttribute" /> class.
/// </summary>
public HttpPostedFileExtensionsAttribute()
: base(DataType.Upload)
{
ErrorMessage = _innerAttribute.ErrorMessage;
}
/// <summary>
/// Gets or sets the file name extensions.
/// </summary>
/// <returns>
/// The file name extensions, or the default file extensions (".png", ".jpg", ".jpeg", and ".gif") if the property is not set.
/// </returns>
public string Extensions
{
get { return _innerAttribute.Extensions; }
set { _innerAttribute.Extensions = value; }
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ValidationType = "extension",
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
};
rule.ValidationParameters["extension"] = _innerAttribute.Extensions;
yield return rule;
}
/// <summary>
/// Applies formatting to an error message, based on the data field where the error occurred.
/// </summary>
/// <returns>
/// The formatted error message.
/// </returns>
/// <param name="name">The name of the field that caused the validation failure.</param>
public override string FormatErrorMessage(string name)
{
return _innerAttribute.FormatErrorMessage(name);
}
/// <summary>
/// Checks that the specified file name extension or extensions is valid.
/// </summary>
/// <returns>
/// true if the file name extension is valid; otherwise, false.
/// </returns>
/// <param name="value">A comma delimited list of valid file extensions.</param>
public override bool IsValid(object value)
{
var file = value as HttpPostedFileBase;
if (file != null)
{
return _innerAttribute.IsValid(file.FileName);
}
return _innerAttribute.IsValid(value);
}
}
I know this is a bit too late, but perhaps this can help someone out there. This is a modified version of @jfeinour, that will work on the client-side as well:
public class HttpPostedFileExtensionAttribute : ValidationAttribute, IClientValidatable {
private readonly FileExtensionsAttribute _fileExtensionsAttribute = new FileExtensionsAttribute();
public HttpPostedFileExtensionAttribute() {
ErrorMessage = _fileExtensionsAttribute.ErrorMessage;
}
public string Extensions {
get { return _fileExtensionsAttribute.Extensions; }
set { _fileExtensionsAttribute.Extensions = value; }
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) {
var rule = new ModelClientValidationRule {
ValidationType = "extension",
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
};
rule.ValidationParameters["extension"] =
_fileExtensionsAttribute.Extensions
.Replace(" ", string.Empty).Replace(".", string.Empty)
.ToLowerInvariant();
yield return rule;
}
public override string FormatErrorMessage(string name) {
return _fileExtensionsAttribute.FormatErrorMessage(name);
}
public override bool IsValid(object value) {
var file = value as HttpPostedFileBase;
return _fileExtensionsAttribute.IsValid(file != null ? file.FileName : value);
}
}
The FileExtensionsAttribute does not know how to verify a HttpPostedFileBase, so I extended it..
/// <summary>
/// A File extensions attribute for verifying the file extensions of posted files from MVC forms.
/// </summary>
public class PostedFileBaseFileExtensionsAttribute : FileExtensionsAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var file = value as HttpPostedFileBase;
if (file == null)
{
return new ValidationResult("No File Specified");
}
return base.IsValid(file.FileName, validationContext);
}
}
Note that this method forces the file to be a required field. Alternatively, if it is an optional field, use the code below for the method body. This always returns success if no file is specified (probably more correct in most cases)..
var file = value as HttpPostedFileBase;
return file == null ? ValidationResult.Success : base.IsValid(file.FileName, validationContext);