Una posibilidad es escribir un atributo de validación personalizada:
public class MaxFileSizeAttribute : ValidationAttribute
{
private readonly int _maxFileSize;
public MaxFileSizeAttribute(int maxFileSize)
{
_maxFileSize = maxFileSize;
}
public override bool IsValid(object value)
{
var file = value as HttpPostedFileBase;
if (file == null)
{
return false;
}
return file.ContentLength <= _maxFileSize;
}
public override string FormatErrorMessage(string name)
{
return base.FormatErrorMessage(_maxFileSize.ToString());
}
}
y entonces podría tener una vista de modelo:
public class MyViewModel
{
[Required]
[MaxFileSize(8 * 1024 * 1024, ErrorMessage = "Maximum allowed file size is {0} bytes")]
public HttpPostedFileBase File { get; set; }
}
controlador:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
// validation failed => redisplay the view
return View(model);
}
// the model is valid => we could process the file here
var fileName = Path.GetFileName(model.File.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
model.File.SaveAs(path);
return RedirectToAction("Success");
}
}
y una vista:
@model MyViewModel
@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.TextBoxFor(x => x.File, new { type = "file" })
@Html.ValidationMessageFor(x => x.File)
<button type="submit">OK</button>
}
Ahora, por supuesto, para que esto funcione, tendrá que aumentar el tamaño máximo permitido de carga de archivos en web.config para un valor suficientemente grande:
<!-- 1GB (the value is in KB) -->
<httpRuntime maxRequestLength="1048576" />
y para IIS7:
<system.webServer>
<security>
<requestFiltering>
<!-- 1GB (the value is in Bytes) -->
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
</system.webServer>
Ahora podríamos llevar nuestro atributo de validación personalizado un paso más allá y permitir la validación del lado del cliente para evitar el desperdicio de ancho de banda. Por supuesto, verificar el tamaño del archivo antes de cargarlo solo es posible con HTML5 File API. Como consecuencia, solo los navegadores compatibles con esta API podrán aprovecharlo.
Así que el primer paso es hacer que nuestra validación de atributos personalizados implementar la interfaz IClientValidatable que nos permitirá utilizar un adaptador de costumbre en javascript:
public class MaxFileSizeAttribute : ValidationAttribute, IClientValidatable
{
private readonly int _maxFileSize;
public MaxFileSizeAttribute(int maxFileSize)
{
_maxFileSize = maxFileSize;
}
public override bool IsValid(object value)
{
var file = value as HttpPostedFileBase;
if (file == null)
{
return false;
}
return file.ContentLength <= _maxFileSize;
}
public override string FormatErrorMessage(string name)
{
return base.FormatErrorMessage(_maxFileSize.ToString());
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(_maxFileSize.ToString()),
ValidationType = "filesize"
};
rule.ValidationParameters["maxsize"] = _maxFileSize;
yield return rule;
}
}
y todo lo que queda es configurar el adaptador personalizado:
jQuery.validator.unobtrusive.adapters.add(
'filesize', [ 'maxsize' ], function (options) {
options.rules['filesize'] = options.params;
if (options.message) {
options.messages['filesize'] = options.message;
}
}
);
jQuery.validator.addMethod('filesize', function (value, element, params) {
if (element.files.length < 1) {
// No files selected
return true;
}
if (!element.files || !element.files[0].size) {
// This browser doesn't support the HTML5 API
return true;
}
return element.files[0].size < params.maxsize;
}, '');
Antes que nada, muchas gracias, Darin. Estoy intentando implementar su solución, pero parece que no puedo usar 'IClientValidatable'. Tengo el System.Web.Mvc ambos se agregaron en las referencias del proyecto y en los usos de la página. ¿Qué estoy haciendo mal? –
No sé lo que estás haciendo mal. El 'IClientValidatable' se agregó en ASP.NET MVC 3 en el ensamblado' System.Web.Mvc.dll' dentro del espacio de nombres 'System.Web.Mvc'. –
Lo verifiqué y me equivoqué al pensar que estábamos usando MVC3, de hecho estamos usando MVC2. Dado que la actualización no es una opción para mí, ¿puede aplicarse alguna parte de esta solución? –