2012-05-04 7 views
29

Pracuję z C# MVC 2 i ASP.NET. Jeden z moich formularzy zawiera pole wprowadzania pliku, które pozwala na użycie dowolnego typu pliku, który następnie zostanie przekonwertowany na obiekt typu blob i zapisany w bazie danych. Moim problemem jest to, że gdy użytkownik wybiera plik, który przekracza pewną amoutn z MB (około 8) pojawia się błąd strony, która mówi, co następuje:Sprawdzanie poprawności dla dużych plików po przesłaniu

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

Nie przeszkadza mi, że istnieje granica 8Mb do pliki, które użytkownicy przesyłają, jednak muszę zatrzymać obecny błąd i wyświetlić odpowiedni komunikat potwierdzający (najlepiej z funkcją ModelState.AddModelError). Czy ktoś może mi pomóc? Nie mogę "złapać" błędu, zanim cokolwiek innego stanie się na stronie, ponieważ dzieje się to zanim dotrze do funkcji przesyłania w kontrolerze.

Odpowiedz

66

Jedną z możliwości jest napisanie atrybut niestandardowy Walidacja:

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

a następnie można mieć model wyświetlania:

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

kontroler:

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

i widok:

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

Teraz oczywiście to zadziałało trzeba będzie zwiększyć maksymalny dopuszczalny rozmiar pliku web.config do wysyłania w dostatecznie dużej wartości:

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

i IIS7:

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

Możemy teraz wprowadzić nasz niestandardowy atrybut sprawdzania poprawności i umożliwić walidację po stronie klienta, aby uniknąć marnowania przepustowości. Oczywiście weryfikacja rozmiaru pliku przed przesłaniem jest możliwa tylko z HTML5 File API. W rezultacie tylko przeglądarki obsługujące ten interfejs API będą mogły z niego korzystać.

Tak więc pierwszym krokiem jest, aby przypisywać nasz zwyczaj walidacja implementować interfejs IClientValidatable który pozwoli nam dołączyć kartę niestandardową w 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; 
    } 
} 

i wszystko, co pozostaje, to skonfigurować adapter zwyczaj:

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

Po pierwsze, dziękuję bardzo Darin. Próbuję zaimplementować Twoje rozwiązanie, ale nie mogę używać "IClientValidatable". Mam System.Web.Mvc zarówno dodane w referencjach projektu, jak i na stronie. Co ja robię źle? –

+0

Nie wiem, co robisz źle. "IClientValidatable" został dodany w ASP.NET MVC 3 w zespole 'System.Web.Mvc.dll' wewnątrz przestrzeni nazw' System.Web.Mvc'. –

+0

Sprawdziłem i pomyliłem się myśląc, że używamy MVC3, w rzeczywistości używamy MVC2. Ponieważ aktualizacja nie jest dla mnie opcją, czy jakakolwiek część tego rozwiązania może nadal obowiązywać? –

1

można zwiększyć długość prośba max dla niektórych adresów URL w pliku web.config:

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