【问题标题】:Validating for large files upon Upload上传时验证大文件
【发布时间】:2012-05-04 09:04:53
【问题描述】:

我正在使用 c# MVC 2 和 ASP.NET。我的一个表单包括一个文件输入字段,允许用户选择任何文件类型,然后将其转换为 blob 并保存到数据库中。我的问题是,每当用户选择的文件超过一定数量的 Mb(大约 8 个)时,我都会收到一个页面错误,内容如下:

The connection was reset
The connection to the server was reset while the page was loading.

我不介意用户上传的文件有 8Mb 的限制,但是我需要阻止当前错误的发生并显示正确的验证消息(最好使用 ModelState.AddModelError 函数)。有谁能够帮我?在页面中发生其他任何事情之前,我似乎无法“捕获”错误,因为它发生在它到达控制器内的上传功能之前。

【问题讨论】:

    标签: c# asp.net validation file-upload asp.net-mvc-2


    【解决方案1】:

    一种可能性是编写自定义验证属性:

    public class MaxFileSizeAttribute : ValidationAttribute
    {
        private readonly int _maxFileSize;
        public MaxFileSizeAttribute(int maxFileSize)
        {
            _maxFileSize = maxFileSize;
        }
    
        public override bool IsValid(object value)
        {
            var file = value as HttpPostedFileBase;
            if (file == null)
            {
                return false;
            }
            return file.ContentLength <= _maxFileSize;
        }
    
        public override string FormatErrorMessage(string name)
        {
            return base.FormatErrorMessage(_maxFileSize.ToString());
        }
    }
    

    然后你可以有一个视图模型:

    public class MyViewModel
    {
        [Required]
        [MaxFileSize(8 * 1024 * 1024, ErrorMessage = "Maximum allowed file size is {0} bytes")]
        public HttpPostedFileBase File { get; set; }
    }
    

    控制器:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new MyViewModel());
        }
    
        [HttpPost]
        public ActionResult Index(MyViewModel model)
        {
            if (!ModelState.IsValid)
            {
                // validation failed => redisplay the view
                return View(model);
            }
    
            // the model is valid => we could process the file here
            var fileName = Path.GetFileName(model.File.FileName);
            var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
            model.File.SaveAs(path);
    
            return RedirectToAction("Success");
        }
    }
    

    还有一个观点:

    @model MyViewModel
    
    @using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        @Html.TextBoxFor(x => x.File, new { type = "file" })
        @Html.ValidationMessageFor(x => x.File)
        <button type="submit">OK</button>
    }
    

    当然,要使其正常工作,您必须将 web.config 中允许的最大上传文件大小增加到足够大的值:

    <!-- 1GB (the value is in KB) -->
    <httpRuntime maxRequestLength="1048576" />
    

    对于 IIS7:

    <system.webServer>
        <security>
            <requestFiltering>
               <!-- 1GB (the value is in Bytes) -->
                <requestLimits maxAllowedContentLength="1073741824" />
            </requestFiltering>
        </security>
    </system.webServer>
    

    我们现在可以将我们的自定义验证属性更进一步,并启用客户端验证以避免浪费带宽。当然,上传前验证文件大小只能使用HTML5 File API。因此,只有支持此 API 的浏览器才能利用它。

    所以第一步是让我们的自定义验证属性实现IClientValidatable接口,这将允许我们在javascript中附加一个自定义适配器:

    public class MaxFileSizeAttribute : ValidationAttribute, IClientValidatable
    {
        private readonly int _maxFileSize;
        public MaxFileSizeAttribute(int maxFileSize)
        {
            _maxFileSize = maxFileSize;
        }
    
        public override bool IsValid(object value)
        {
            var file = value as HttpPostedFileBase;
            if (file == null)
            {
                return false;
            }
            return file.ContentLength <= _maxFileSize;
        }
    
        public override string FormatErrorMessage(string name)
        {
            return base.FormatErrorMessage(_maxFileSize.ToString());
        }
    
        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule
            {
                ErrorMessage = FormatErrorMessage(_maxFileSize.ToString()),
                ValidationType = "filesize"
            };
            rule.ValidationParameters["maxsize"] = _maxFileSize;
            yield return rule;
        }
    }
    

    剩下的就是配置自定义适配器:

    jQuery.validator.unobtrusive.adapters.add(
        'filesize', [ 'maxsize' ], function (options) {
            options.rules['filesize'] = options.params;
            if (options.message) {
                options.messages['filesize'] = options.message;
            }
        }
    );
    
    jQuery.validator.addMethod('filesize', function (value, element, params) {
        if (element.files.length < 1) {
            // No files selected
            return true;
        }
    
        if (!element.files || !element.files[0].size) {
            // This browser doesn't support the HTML5 API
            return true;
        }
    
        return element.files[0].size < params.maxsize;
    }, '');
    

    【讨论】:

    • 首先,非常感谢达林。我正在尝试实施您的解决方案,但是我似乎无法使用“IClientValidatable”。我在项目的引用和页面使用中都添加了 System.Web.Mvc。我做错了什么?
    • 我不知道你做错了什么。 IClientValidatable 已添加到 ASP.NET MVC 3 中 System.Web.Mvc.dll 程序集的 System.Web.Mvc 命名空间内。
    • 很棒的答案!不过,有一个问题。 MaxFileSizeAttribute.IsValid 在值为 null 时返回 false,实际上需要上传文件。
    • 在您的客户端 javascript 验证中存在一个小错误。最后一行应该是“return element.files[0].size
    • 为了防止需要检查,只需输入“IsValid”:if (file == null) return true;
    【解决方案2】:

    您可以在 web.config 中增加某些 url 的请求最大长度:

    <location path="fileupload">
      <system.web>
        <httpRuntime executionTimeout="600" maxRequestLength="10485760" />
      </system.web>
    </location>
    

    【讨论】:

      【解决方案3】:

      我搜索了一下,发现了以下两个 URL,它们似乎表明这个问题或异常最好在 Global.asax 中的应用程序错误事件中处理。

      1. Catching 'Maximum request length exceeded'
      2. How to handle "Maximum request length exceeded" exception

      【讨论】:

        猜你喜欢
        • 2011-09-05
        • 2011-06-24
        • 1970-01-01
        • 1970-01-01
        • 2011-04-12
        • 2012-04-12
        • 1970-01-01
        相关资源
        最近更新 更多