2012-05-04 22 views
29

Estoy trabajando con C# MVC 2 y ASP.NET. Uno de mis formularios incluye un campo de entrada de archivo que permite un uso para seleccionar cualquier tipo de archivo que luego se convertirá en un blob y se guardará en la base de datos. Mi problema es que cada vez que un usuario selecciona un archivo que supera un cierto amoutn de Mb (aproximadamente 8) me sale un error de página que dice lo siguiente:Validación para archivos grandes al Cargar

The connection was reset 
The connection to the server was reset while the page was loading. 

no me importa que hay un límite de 8 MB a la archivos que los usuarios están cargando; sin embargo, debo detener el error actual y mostrar un mensaje de validación adecuado (preferiblemente con la función ModelState.AddModelError). Alguien puede ayudarme? Parece que no puedo 'detectar' el error antes de que ocurra algo más en la página, ya que está sucediendo antes de que llegue a la función de carga dentro del controlador.

Respuesta

66

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; 
}, ''); 
+0

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? –

+0

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'. –

+0

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? –

1

puede aumentar petición longitud máxima para determinadas direcciones URL en web.config:

<location path="fileupload"> 
    <system.web> 
    <httpRuntime executionTimeout="600" maxRequestLength="10485760" /> 
    </system.web> 
</location> 
Cuestiones relacionadas