¿Cómo validar el archivo cargado en ASP.NET MVC?

Tengo una acción Crear que toma un objeto de entidad y una imagen HttpPostedFileBase. La imagen no pertenece al modelo de entidad.

Puedo guardar el objeto de la entidad en la base de datos y el archivo en el disco, pero no estoy seguro de cómo validar estas reglas comerciales:

  • La imagen es obligatoria
  • El tipo de contenido debe ser “image / png”
  • No debe exceder 1MB

Un atributo de validación personalizado es una forma de hacerlo:

public class ValidateFileAttribute : RequiredAttribute { public override bool IsValid(object value) { var file = value as HttpPostedFileBase; if (file == null) { return false; } if (file.ContentLength > 1 * 1024 * 1024) { return false; } try { using (var img = Image.FromStream(file.InputStream)) { return img.RawFormat.Equals(ImageFormat.Png); } } catch { } return false; } } 

y luego aplicar en su modelo:

 public class MyViewModel { [ValidateFile(ErrorMessage = "Please select a PNG image smaller than 1MB")] public HttpPostedFileBase File { get; set; } } 

El controlador podría verse así:

 public class HomeController : Controller { public ActionResult Index() { var model = new MyViewModel(); return View(model); } [HttpPost] public ActionResult Index(MyViewModel model) { if (!ModelState.IsValid) { return View(model); } // The uploaded image corresponds to our business rules => process it var fileName = Path.GetFileName(model.File.FileName); var path = Path.Combine(Server.MapPath("~/App_Data"), fileName); model.File.SaveAs(path); return Content("Thanks for uploading", "text/plain"); } } 

y la vista:

 @model MyViewModel @using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" })) { @Html.LabelFor(x => x.File)  @Html.ValidationMessageFor(x => x.File)  } 

Sobre la base de la respuesta de Darin Dimitrov que encontré muy útil, tengo una versión adaptada que permite verificaciones de múltiples tipos de archivos, que es lo que estaba buscando inicialmente.

 public override bool IsValid(object value) { bool isValid = false; var file = value as HttpPostedFileBase; if (file == null || file.ContentLength > 1 * 1024 * 1024) { return isValid; } if (IsFileTypeValid(file)) { isValid = true; } return isValid; } private bool IsFileTypeValid(HttpPostedFileBase file) { bool isValid = false; try { using (var img = Image.FromStream(file.InputStream)) { if (IsOneOfValidFormats(img.RawFormat)) { isValid = true; } } } catch { //Image is invalid } return isValid; } private bool IsOneOfValidFormats(ImageFormat rawFormat) { List formats = getValidFormats(); foreach (ImageFormat format in formats) { if(rawFormat.Equals(format)) { return true; } } return false; } private List getValidFormats() { List formats = new List(); formats.Add(ImageFormat.Png); formats.Add(ImageFormat.Jpeg); formats.Add(ImageFormat.Gif); //add types here return formats; } } 

Aquí hay una manera de hacerlo usando viewmodel, eche un vistazo al código completo aquí

Validación de archivos Asp.Net MVC para tamaño y tipo Cree un modelo de vista como se muestra a continuación con FileSize y FileTypes

 public class ValidateFiles { [FileSize(10240)] [FileTypes("doc,docx,xlsx")] public HttpPostedFileBase File { get; set; } } 

Crear atributos personalizados

 public class FileSizeAttribute : ValidationAttribute { private readonly int _maxSize; public FileSizeAttribute(int maxSize) { _maxSize = maxSize; } //..... //..... } public class FileTypesAttribute : ValidationAttribute { private readonly List _types; public FileTypesAttribute(string types) { _types = types.Split(',').ToList(); } //.... //... } 

También puede considerar guardar la imagen en la base de datos:

  using (MemoryStream mstream = new MemoryStream()) { if (context.Request.Browser.Browser == "IE") context.Request.Files[0].InputStream.CopyTo(mstream); else context.Request.InputStream.CopyTo(mstream); if (ValidateIcon(mstream)) { Icon icon = new Icon() { ImageData = mstream.ToArray(), MimeType = context.Request.ContentType }; this.iconRepository.SaveOrUpdate(icon); } } 

Lo uso con NHibernate – entidad definida:

  public Icon(int id, byte[] imageData, string mimeType) { this.Id = id; this.ImageData = imageData; this.MimeType = mimeType; } public virtual byte[] ImageData { get; set; } public virtual string MimeType { get; set; } 

Luego puede devolver la imagen como FileContentResult:

  public FileContentResult GetIcon(int? iconId) { try { if (!iconId.HasValue) return null; Icon icon = this.iconRepository.Get(iconId.Value); return File(icon.ImageData, icon.MimeType); } catch (Exception ex) { Log.ErrorFormat("ImageController: GetIcon Critical Error: {0}", ex); return null; } } 

Tenga en cuenta que esto está usando ajax submit. Más fácil de acceder a la secuencia de datos de lo contrario.