2012-05-04 23 views
29

Sto lavorando con C# MVC 2 e ASP.NET. Una delle mie forme include un campo di input del file che consente di selezionare qualsiasi tipo di file che verrà poi convertito in un blob e salvato nel database. Il mio problema è che ogni volta che un utente seleziona un file che supera un certo Amoutn di Mb (circa 8) ottengo un errore di pagina che dice quanto segue:Convalida per file di grandi dimensioni al caricamento

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

non mi dispiace che ci sia un limite 8Mb al file che gli utenti stanno caricando, tuttavia ho bisogno di fermare l'errore corrente e visualizzare un messaggio di convalida appropriato (preferibilmente con la funzione ModelState.AddModelError). Qualcuno può aiutarmi? Non riesco a 'cogliere' l'errore prima che accada qualcos'altro nella pagina da quando sta accadendo prima che arrivi nella funzione di upload all'interno del controller.

risposta

66

Una possibilità è quello di scrivere un attributo di convalida personalizzato:

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()); 
    } 
} 

e poi si potrebbe avere un modello di vista:

public class MyViewModel 
{ 
    [Required] 
    [MaxFileSize(8 * 1024 * 1024, ErrorMessage = "Maximum allowed file size is {0} bytes")] 
    public HttpPostedFileBase File { get; set; } 
} 

regolatore:

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"); 
    } 
} 

e 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> 
} 

Ora, naturalmente, per questo al lavoro si dovrà aumentare la dimensione massima consentita file di caricamento nel web.config per un valore sufficientemente grande:

<!-- 1GB (the value is in KB) --> 
<httpRuntime maxRequestLength="1048576" /> 

e per IIS7:

<system.webServer> 
    <security> 
     <requestFiltering> 
      <!-- 1GB (the value is in Bytes) --> 
      <requestLimits maxAllowedContentLength="1073741824" /> 
     </requestFiltering> 
    </security> 
</system.webServer> 

Ora possiamo portare il nostro attributo di convalida personalizzato un ulteriore passo avanti e abilitare la convalida lato client per evitare lo spreco di larghezza di banda. Naturalmente verificare la dimensione del file prima del caricamento è possibile solo con HTML5 File API. Di conseguenza, solo i browser che supportano questa API potranno trarne vantaggio.

Quindi il primo passo è quello di rendere attribuiamo il nostro convalida personalizzata implementare l'interfaccia IClientValidatable che ci permetterà di allegare un adattatore personalizzato in 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; 
    } 
} 

e tutto quello che resta è configurare l'adattatore personalizzato:

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

Prima di tutto, grazie mille Darin. Sto tentando di implementare la tua soluzione, ma non riesco a utilizzare IClientValidatable. Ho il System.Web.Sia Mvc aggiunto nei riferimenti del progetto e negli usi della pagina. Che cosa sto facendo di sbagliato? –

+0

Non so cosa stai facendo male. Il 'IClientValidatable' è stato aggiunto in ASP.NET MVC 3 nell'assembly' System.Web.Mvc.dll' all'interno dello spazio dei nomi 'System.Web.Mvc'. –

+0

Ho verificato e mi sono sbagliato a pensare che stessimo usando MVC3, stiamo infatti utilizzando MVC2. Poiché l'aggiornamento non è un'opzione per me, è possibile applicare una parte qualsiasi di questa soluzione? –

1

È possibile aumentare la lunghezza richiesta massima per determinati URL nel web.config:

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