【问题标题】:How to validate uploaded file in ASP.NET MVC?如何在 ASP.NET MVC 中验证上传的文件?
【发布时间】:2011-06-17 16:29:47
【问题描述】:

我有一个 Create 操作,它采用一个实体对象和一个 HttpPostedFileBase 图像。图片不属于实体模型。

我可以将实体对象保存在数据库中,将文件保存在磁盘中,但我不确定如何验证这些业务规则:

  • 图片为必填项
  • 内容类型必须是“image/png”
  • 不得超过 1MB

【问题讨论】:

    标签: asp.net-mvc security


    【解决方案1】:

    自定义验证属性是一种方法:

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

    然后应用到您的模型上:

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

    控制器可能如下所示:

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

    和视图:

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

    【讨论】:

    • ImageFormat 的 +1 - 解决了一个我还没想过的问题!
    • 很棒的东西。为我节省了很多时间。
    • 对于 PDF,Image.FromStream 的等效项是什么?
    • @escist,没有。 .NET 框架不支持 PDF 格式。如果您想操作 PDF 文件,您将不得不使用第三方库。 iTextSharp 是一个流行的开源库。还有许多其他商业图书馆。如果您不想使用第三方库,您可以查看流的第一个字节以确定它是否是有效的 PDF。请记住,PDF 格式有不同的版本,如果您想要一个通用的解决方案,您必须考虑到它们。
    • @DarinDimitrov 你知道它是否适用于客户端验证吗?
    【解决方案2】:

    根据我发现非常有帮助的 Darin Dimitrov 的回答,我有一个改编版本,它允许检查多种文件类型,这正是我最初寻找的。

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

    【讨论】:

    • 这行不通,因为'var file = value as HttpPostedFileBase;'在方法 isValid 中将返回 false,因为值的类型为数组 HttpPostedFileBase[]; @伊丽莎白哈姆雷特
    • 请帮忙。在 IsValid 函数中,我得到 null 的值,因此也得到 null 的文件。以下几行来自我的 ViewModel: [ValidateFile(ErrorMessage = "Please select an image file")] [DataType(DataType.Upload)] [DisplayName("File")] public HttpPostedFileBase FileAttachment { get;放; } 谢谢。
    • 没关系让它工作。我的控制器中的 [Bind(Include = "...")] 中没有 FileAttachment 属性。
    【解决方案3】:

    这是一种使用 viewmodel 的方法,在此处查看整个代码

    Asp.Net MVC file validation for size and type 使用 FileSize 和 FileTypes 创建如下所示的视图模型

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

    创建自定义属性

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

    【讨论】:

    • 这对我有用,但是在提交表单时,验证只有在到达服务器后才有效,你知道如何让它只在客户端工作,这样它就可以像 [Required] 属性一样工作吗?
    【解决方案4】:

    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
    }
    

    【讨论】:

      【解决方案5】:

      您可能还需要考虑将图像保存到数据库:

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

      我将它与 NHibernate 一起使用 - 实体定义:

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

      然后您可以将图像作为 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;
              }
          }
      

      请注意,这是使用 ajax 提交。否则更容易访问数据流。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-10-28
        • 2012-08-28
        • 2010-12-27
        • 2012-10-30
        • 2010-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多