【发布时间】:2011-06-17 16:29:47
【问题描述】:
我有一个 Create 操作,它采用一个实体对象和一个 HttpPostedFileBase 图像。图片不属于实体模型。
我可以将实体对象保存在数据库中,将文件保存在磁盘中,但我不确定如何验证这些业务规则:
- 图片为必填项
- 内容类型必须是“image/png”
- 不得超过 1MB
【问题讨论】:
标签: asp.net-mvc security
我有一个 Create 操作,它采用一个实体对象和一个 HttpPostedFileBase 图像。图片不属于实体模型。
我可以将实体对象保存在数据库中,将文件保存在磁盘中,但我不确定如何验证这些业务规则:
【问题讨论】:
标签: asp.net-mvc security
自定义验证属性是一种方法:
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" />
}
【讨论】:
Image.FromStream 的等效项是什么?
iTextSharp 是一个流行的开源库。还有许多其他商业图书馆。如果您不想使用第三方库,您可以查看流的第一个字节以确定它是否是有效的 PDF。请记住,PDF 格式有不同的版本,如果您想要一个通用的解决方案,您必须考虑到它们。
根据我发现非常有帮助的 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;
}
}
【讨论】:
这是一种使用 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();
}
//....
//...
}
【讨论】:
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
}
【讨论】:
您可能还需要考虑将图像保存到数据库:
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 提交。否则更容易访问数据流。
【讨论】: