使用 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有三种方式(不使用,动态使用,静态使用,默认是动态使用)