70 votes

Comment valider un fichier téléchargé en ASP.NET MVC ?

J'ai une action Create qui prend un objet entité et une image HttpPostedFileBase. L'image n'appartient pas au modèle d'entité.

Je peux enregistrer l'objet entité dans la base de données et le fichier sur le disque, mais je ne sais pas comment valider ces règles de gestion :

  • L'image est requise
  • Le type de contenu doit être "image/png".
  • Ne doit pas dépasser 1MB

140voto

Darin Dimitrov Points 528142

Un attribut de validation personnalisé est une solution :

public class ValidateFileAttribute : RequiredAttribute
{
    public override bool IsValid(object value)
    {
        var file = value as HttpPostedFileBase;
        if (file == null)
        {
            return false;
        }

        if (file.ContentLength > 1 * 1024 * 1024)
        {
            return false;
        }

        try
        {
            using (var img = Image.FromStream(file.InputStream))
            {
                return img.RawFormat.Equals(ImageFormat.Png);
            }
        }
        catch { }
        return false;
    }
}

et ensuite appliquer sur votre modèle :

public class MyViewModel
{
    [ValidateFile(ErrorMessage = "Please select a PNG image smaller than 1MB")]
    public HttpPostedFileBase File { get; set; }
}

Le contrôleur pourrait ressembler à ceci :

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel();
        return View(model);
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // The uploaded image corresponds to our business rules => process it

        var fileName = Path.GetFileName(model.File.FileName);
        var path = Path.Combine(Server.MapPath("~/App_Data"), fileName);
        model.File.SaveAs(path);

        return Content("Thanks for uploading", "text/plain");
    }
}

et la vue :

@model MyViewModel

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
    @Html.LabelFor(x => x.File)
    <input type="file" name="@Html.NameFor(x => x.File)" id="@Html.IdFor(x => x.File)" />
    @Html.ValidationMessageFor(x => x.File)
    <input type="submit" value="upload" />
}

1 votes

+1 pour le truc sur le format d'image - cela résout un problème auquel je n'avais pas encore pensé !

0 votes

C'est génial. J'ai gagné beaucoup de temps.

0 votes

Quel serait l'équivalent de Image.FromStream pour les PDF ?

9voto

Elizabeth Hamlet Points 106

Sur la base de la réponse de Darin Dimitrov, que j'ai trouvée très utile, j'ai adapté une version qui permet de vérifier plusieurs types de fichiers, ce qui est ce que je recherchais initialement.

public override bool IsValid(object value)
    {
        bool isValid = false;
        var file = value as HttpPostedFileBase;

        if (file == null || file.ContentLength > 1 * 1024 * 1024)
        {
            return isValid;
        }

        if (IsFileTypeValid(file))
        {
            isValid = true;
        }

        return isValid;
    }

    private bool IsFileTypeValid(HttpPostedFileBase file)
    {
        bool isValid = false;

        try
        {
            using (var img = Image.FromStream(file.InputStream))
            {
                if (IsOneOfValidFormats(img.RawFormat))
                {
                    isValid = true;
                } 
            }
        }
        catch 
        {
            //Image is invalid
        }
        return isValid;
    }

    private bool IsOneOfValidFormats(ImageFormat rawFormat)
    {
        List<ImageFormat> formats = getValidFormats();

        foreach (ImageFormat format in formats)
        {
            if(rawFormat.Equals(format))
            {
                return true;
            }
        }
        return false;
    }

    private List<ImageFormat> getValidFormats()
    {
        List<ImageFormat> formats = new List<ImageFormat>();
        formats.Add(ImageFormat.Png);
        formats.Add(ImageFormat.Jpeg);
        formats.Add(ImageFormat.Gif);
        //add types here
        return formats;
    }
}

0 votes

Cela ne fonctionnera pas car la ligne 'var file = value as HttpPostedFileBase;' dans la méthode isValid renverra false car value est de type array HttpPostedFileBase[] ; @Elizabeth Hamlet

0 votes

Aidez-nous. Dans la fonction IsValid, j'obtiens null pour la valeur et donc null pour le fichier également. Les lignes suivantes proviennent de mon ViewModel : [ValidateFile(ErrorMessage = "Veuillez sélectionner un fichier image")] [DataType(DataType.Upload)] [DisplayName("File")] public HttpPostedFileBase FileAttachment { get ; set ; } Merci.

0 votes

Peu importe, j'ai réussi à le faire fonctionner. Je n'ai pas la propriété FileAttachment dans [Bind(Include = "...")] dans mon contrôleur.

4voto

Jeff D Points 319

Voici une façon de le faire en utilisant le viewmodel, jetez un coup d'oeil au code complet ici.

Validation de la taille et du type des fichiers en Asp.Net MVC Créez un modèle de vue comme indiqué ci-dessous avec FileSize et FileTypes.

public class ValidateFiles
{
    [FileSize(10240)]
    [FileTypes("doc,docx,xlsx")]
    public HttpPostedFileBase File { get; set; }
}

Créer des attributs personnalisés

public class FileSizeAttribute : ValidationAttribute
{
    private readonly int _maxSize;

    public FileSizeAttribute(int maxSize)
    {
        _maxSize = maxSize;
    }
    //.....
    //.....
}

public class FileTypesAttribute : ValidationAttribute
{
    private readonly List<string> _types;

    public FileTypesAttribute(string types)
    {
        _types = types.Split(',').ToList();
    } 
    //....
    //...
}

1 votes

Cela fonctionne pour moi, mais pour l'envoi du formulaire, la validation ne fonctionne qu'après avoir été envoyée au serveur. Savez-vous comment faire en sorte que cela fonctionne uniquement du côté client, exactement comme l'attribut [Required] ?

1voto

Evgeny Levin Points 1970

Et la validation de la longueur du fichier dans asp.net core :

public async Task<IActionResult> MyAction()
{
    var form = await Request.ReadFormAsync();
    if (form.Files != null && form.Files.Count == 1)
    {
        var file = form.Files[0];
        if (file.Length > 1 * 1024 * 1024)
        {
            ModelState.AddModelError(String.Empty, "Maximum file size is 1 Mb.");
        }
    }
    // action code goes here
}

-1voto

BonyT Points 6465

Vous pouvez également envisager d'enregistrer l'image dans la base de données :

  using (MemoryStream mstream = new MemoryStream())
                {
                    if (context.Request.Browser.Browser == "IE")
                        context.Request.Files[0].InputStream.CopyTo(mstream);
                    else
                        context.Request.InputStream.CopyTo(mstream);

                    if (ValidateIcon(mstream))
                    {
                        Icon icon = new Icon() { ImageData = mstream.ToArray(), MimeType = context.Request.ContentType };
                        this.iconRepository.SaveOrUpdate(icon);
                    }
                }

Je l'utilise avec NHibernate - entité définie :

 public Icon(int id, byte[] imageData, string mimeType)
    {
        this.Id = id;
        this.ImageData = imageData;
        this.MimeType = mimeType;
    }

    public virtual byte[] ImageData { get; set; }

    public virtual string MimeType { get; set; }

Vous pouvez ensuite renvoyer l'image comme un FileContentResult :

 public FileContentResult GetIcon(int? iconId)
    {
        try
        {
            if (!iconId.HasValue) return null;

            Icon icon = this.iconRepository.Get(iconId.Value);

            return File(icon.ImageData, icon.MimeType);
        }
        catch (Exception ex)
        {
            Log.ErrorFormat("ImageController: GetIcon Critical Error: {0}", ex);
            return null;
        }
    }

Notez que cela utilise la soumission ajax. Il est plus facile d'accéder au flux de données autrement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X