使用 ValidateFileAttribute 验证图像

Posted

技术标签:

【中文标题】使用 ValidateFileAttribute 验证图像【英文标题】:Validation for images with ValidateFileAttribute 【发布时间】:2014-12-23 16:32:00 【问题描述】:

我有一个名为:ValidateFileAttribute 的文件,用于验证图像上传的正确输入。像这样:

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.Jpeg);
                
            
            catch  
            return false;
        

    

这是模型的属性:

[DisplayName("Image")]
    [ValidateFile(ErrorMessage = "Please select a PNG image smaller than 1MB")]
    public byte[] Image  get; set; 

这是我的观点:

<div id="upload-choices">
                    <div class="editor-label">
                        @html.LabelFor(m => m.Image)

                        @Html.ValidationMessageFor(model => model.Image)
                    </div>
                    <div class="editor-row">
                        @Html.ValidationSummary(true)
                    </div>
                </div>

但是每次我尝试上传图片时(它是 png 并且小于 1 mb)我都会收到这个错误:

var file = value as HttpPostedFileBase; 文件为空。

谢谢

这是完整的视图:

<div id="tabs-2">
     @using (Html.BeginForm("EditPhotos", "Account", FormMethod.Post, new  id = "form", enctype = "multipart/form-data" ))
        
            @Html.AntiForgeryToken()

            <div class="form-horizontal">
                <h4>Photos</h4>
                <hr />

                @Html.HiddenFor(model => model.Id)


                <div class="editor-label">
                    @Html.LabelFor(model => model.DisplayItem)
                </div>
                <div class="editor-field">
                    @Html.EditorFor(model => model.DisplayItem)

                </div>

                <div id="upload-choices">
                    <div class="editor-label">
                        @Html.LabelFor(m => m.Image)

                        @Html.ValidationMessageFor(model => model.Image)
                    </div>
                    <div class="editor-row">
                        @Html.ValidationSummary(true)
                    </div>
                </div>

                <br />

                <div class="table-responsive">
                    <table class="table">

                        <tr>
                            <th><img   src="@Url.Action("GetImage", "Account", new  id =  Model.Id )"></th>


                        </tr>
                    </table>
                </div>

                <input type="file" name="file" class="filestyle" data-buttontext="Find file">



                <br />
                <div class="progress progress-striped">
                    <div class="progress-bar progress-bar-success">0%</div>
                </div>

                <div id="status"></div>


                <br />

                @*@Html.ActionLink("Upload photos", "Upload")*@
                <div class="pull-left">
                    <div class="col-md-offset-0">
                        <input type="submit" value="Save"  accept="image/x-png, image/gif, image/jpeg" class="btn btn-default pull-left" />

                    </div>
                </div>

            </div>
        

        <br /><br />
        <div>
            @Html.ActionLink("Back to List", "Index")
        </div>
    </div>

这是动作方法:

[HttpPost]
    public ActionResult EditPhotos(UserProfile userprofile, HttpPostedFileBase file)
    
        if (file != null)
        
            // extract only the fielname
            var fileName = Path.GetFileName(file.FileName);
            // store the file inside ~/App_Data/uploads folder
            var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
            file.SaveAs(path);
            ModelState.Clear();
        

        if (ModelState.IsValid)
        
            string username = User.Identity.Name;
            // Get the userprofile
            UserProfile user = db.userProfiles.FirstOrDefault(u => u.UserName.Equals(username));

            // Update fields
            user.Image = new byte[file.ContentLength];
            file.InputStream.Read(user.Image, 0, file.ContentLength);
            user.ImageMimeType = file.ContentType;
            db.Entry(user).State = EntityState.Modified;
            try
            
                db.SaveChanges();
            
            catch (DbEntityValidationException e)
            
                foreach (var eve in e.EntityValidationErrors)
                
                    Console.WriteLine("Entity of type \"0\" in state \"1\" has the following validation errors:",
                        eve.Entry.Entity.GetType().Name, eve.Entry.State);
                    foreach (var ve in eve.ValidationErrors)
                    
                        Console.WriteLine("- Property: \"0\", Error: \"1\"",
                            ve.PropertyName, ve.ErrorMessage);
                    
                
                throw;
            
        
        return RedirectToAction("Edit", routeValues: new  controller = "Account", activetab = "tabs-2" );
    

并且没有:[ValidateFile(ErrorMessage = "请选择小于 1MB 的 PNG 图像")] 我可以上传图像。但当然必须有验证

我现在是这样的:

public override bool IsValid(object value)
        
            var ImageProfile = value as byte[];
            if (ImageProfile == null)
            
                return false;
            

            if (ImageProfile.ContentLength > 1 * 1024 * 1024)
            
                return false;
            

            try
            
                using (var img = Image.FromStream(ImageProfile.InputStream))
                
                    return img.RawFormat.Equals(ImageFormat.Jpeg);
                
            
            catch  
            return false;
        

和属性:

[DisplayName("ImageProfile")]
        [ValidateFile(ErrorMessage = "Please select a PNG image smaller than 1MB")]
        public byte[] ImageProfile  get; set; 

好吧,我现在是这样的:

public override bool IsValid(object value)
        
            var ImageProfile = value as byte[];
            if (ImageProfile == null)
            
                return false;
            

            if (ImageProfile.Length > 1 * 1024 * 1024)
            
                return false;
            

            try
            
               using (var binaryReader = new BinaryReader(HttpContext.Current.Request.Files[0].InputStream))
                
                    //return img.RawFormat.Equals(ImageFormat.Jpeg);
                    ImageProfile = binaryReader.ReadBytes(HttpContext.Current.Request.Files[0].ContentLength);
                
            
            catch  
            return false;
        

但 ImageProfile 仍然为空??怎么可能?

如果我改变这个:

[DisplayName("ImageProfile")]
[ValidateFile(ErrorMessage = "Please select a PNG image smaller than 1MB")]
public HttpPostedFileBase ImageProfile  get; set; 

那么这将不再起作用:

             user.ImageProfile = new byte[file.ContentLength];
            file.InputStream.Read(user.ImageProfile, 0, file.ContentLength);

在这个方法中:

[HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult EditPhotos(UserProfile userprofile, HttpPostedFileBase file)
        
            if (file != null)
            
                // extract only the fielname
                var fileName = Path.GetFileName(file.FileName);
                // store the file inside ~/App_Data/uploads folder
                var path = Path.Combine(Server.MapPath(@"\\Images"), fileName);
                file.SaveAs(path);
                ModelState.Clear();
            

            if (ModelState.IsValid)
            
                string username = User.Identity.Name;
                // Get the userprofile
                UserProfile user = db.userProfiles.FirstOrDefault(u => u.UserName.Equals(username));

                // Update fields
                user.ImageProfile = new byte[file.ContentLength];
                file.InputStream.Read(user.ImageProfile, 0, file.ContentLength);
                user.ImageMimeType = file.ContentType;

                db.Entry(user).State = EntityState.Modified;

                try
                
                    db.SaveChanges();
                
                catch (DbEntityValidationException e)
                
                    foreach (var eve in e.EntityValidationErrors)
                    
                        Console.WriteLine("Entity of type \"0\" in state \"1\" has the following validation errors:",
                            eve.Entry.Entity.GetType().Name, eve.Entry.State);
                        foreach (var ve in eve.ValidationErrors)
                        
                            Console.WriteLine("- Property: \"0\", Error: \"1\"",
                                ve.PropertyName, ve.ErrorMessage);
                        
                    
                    throw;
                              

            

            return RedirectToAction("Edit", routeValues: new  controller = "Account", activetab = "tabs-2" );
        

还有这个:

public ActionResult GetImage(int id) var image = db.userProfiles.Where(p => p.Id == id).Select(img => img.ImageProfile).FirstOrDefault();

        var stream = new MemoryStream(image.ToArray());

        return new FileStreamResult(stream, "image/jpeg");
    

但在这一行仍然报错:var stream = new MemoryStream(image.ToArray());

【问题讨论】:

也许您实际上并未上传文件。我没有看到表单元素,因此我不能说你是否做对了。表单的内容类型应为multipart/form-data。你调试过你的代码吗?你属性中的value真的是null吗? 感谢您的评论,我更新了我的帖子 您模型上的属性是byte[],但您的验证器试图将值转换为HttpPostedFileBase,您不能这样做,值的类型需要与类型兼容在您的模型上,即更改您的验证器以使用字节数组.. 您的input[type=file] 被命名为file,它会自动绑定到控制器中的file 对象。没有与您的 Image 属性相关的输入。 是的,但如果我尝试这个:var file = value as byte[];我不能做这个文件。ContentLength 【参考方案1】:

但 ImageProfile 仍然为空??怎么可能?

我在您的视图中看不到name="ImageProfile" 的输入字段。所以这个属性为空是正常的。所以你可以先解决这个问题:

<input type="file" name="ImageProfile" class="filestyle" data-buttontext="Find file">

那么您应该知道,ASP.NET MVC 默认模型绑定器知道如何将此文件输入转换为HttpPostedFileBase 类型,而不是byte[]

所以这将是您需要在视图模型中修复的下一个问题:

[DisplayName("ImageProfile")]
[ValidateFile(ErrorMessage = "Please select a PNG image smaller than 1MB")]
public HttpPostedFileBase ImageProfile  get; set; 

并且显然在您的验证属性中反映了这种变化:

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;

最后在你的控制器动作中去掉这个文件参数,因为它不再被使用:

[HttpPost]
public ActionResult EditPhotos(UserProfile userprofile)

    ...

【讨论】:

谢谢达林!!为您的答案。看我上面的编辑 您的控制器操作应该将视图模型作为参数,而不是实际的 EntityFramework 模型。视图模型应该使用 HttpPostedFileBase 并且在将其传递给数据库之前,您可以将其映射到 EF 模型中的 byte[]

以上是关于使用 ValidateFileAttribute 验证图像的主要内容,如果未能解决你的问题,请参考以下文章

第一篇 用于测试使用

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份