web-dev-qa-db-fra.com

Validation pour les fichiers volumineux lors du téléchargement

Je travaille avec c # MVC 2 et ASP.NET. Un de mes formulaires comprend un champ de saisie de fichier qui permet de sélectionner n'importe quel type de fichier qui sera ensuite converti en blob et enregistré dans la base de données. Mon problème est que chaque fois qu'un utilisateur sélectionne un fichier qui dépasse une certaine quantité de Mb (environ 8), j'obtiens une erreur de page qui dit ce qui suit:

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

Cela ne me dérange pas qu'il y ait une limite de 8 Mo aux fichiers que les utilisateurs téléchargent, mais je dois empêcher l'erreur actuelle de se produire et afficher un message de validation approprié (de préférence avec la fonction ModelState.AddModelError). Quelqu'un peut-il m'aider? Je ne peux pas sembler `` attraper '' l'erreur avant que quoi que ce soit d'autre se produise dans la page car elle se produit avant qu'elle n'arrive dans la fonction de téléchargement dans le contrôleur.

31
William Calleja

Une possibilité consiste à écrire un attribut de validation personnalisé:

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

puis vous pourriez avoir un modèle de vue:

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

manette:

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

et une vue:

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

Maintenant, bien sûr, pour que cela fonctionne, vous devrez augmenter la taille maximale autorisée du fichier de téléchargement dans web.config à une valeur suffisamment grande:

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

et pour IIS7:

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

Nous pourrions maintenant aller plus loin dans notre attribut de validation personnalisé et activer la validation côté client pour éviter de gaspiller la bande passante. Bien sûr, la vérification de la taille du fichier avant le téléchargement n'est possible qu'avec HTML5 File API . Par conséquent, seuls les navigateurs qui prennent en charge cette API pourront en profiter.

La première étape consiste donc à faire en sorte que notre attribut de validation personnalisé implémente l'interface IClientValidatable qui nous permettra de joindre un adaptateur personnalisé 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;
    }
}

et tout ce qui reste est de configurer l'adaptateur personnalisé:

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;
}, '');
69
Darin Dimitrov

Vous pouvez augmenter la longueur maximale des requêtes pour certaines URL dans web.config:

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