Validating for large files upon Upload
Solution 1
One possibility is to write a custom validation attribute:
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());
}
}
and then you could have a view model:
public class MyViewModel
{
[Required]
[MaxFileSize(8 * 1024 * 1024, ErrorMessage = "Maximum allowed file size is {0} bytes")]
public HttpPostedFileBase File { get; set; }
}
controller:
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");
}
}
and a view:
@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>
}
Now of course for this to work you will have to increase the maximum allowed upload file size in web.config to a sufficiently large value:
<!-- 1GB (the value is in KB) -->
<httpRuntime maxRequestLength="1048576" />
and for IIS7:
<system.webServer>
<security>
<requestFiltering>
<!-- 1GB (the value is in Bytes) -->
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
</system.webServer>
We could now bring our custom validation attribute a step further and enable client side validation to avoid wasting bandwidth. Of course verifying the file size before uploading is only possible with HTML5 File API. As a consequence only browsers that support this API will be able to take advantage of it.
So the first step is to make our custom validation attribute implement the IClientValidatable interface which will allow us to attach a custom adapter in 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;
}
}
and all that's left is configure the custom adapter:
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;
}, '');
Solution 2
You can increase request max length for certain urls in web.config:
<location path="fileupload">
<system.web>
<httpRuntime executionTimeout="600" maxRequestLength="10485760" />
</system.web>
</location>
William Calleja
I'm a Web developer based in Malta interested in developing my own controls.
Updated on April 01, 2020Comments
-
William Calleja about 4 years
I am working with c# MVC 2 and ASP.NET. One of my forms includes a file input field which allows a use to select any file type which will then be converted into a blob and saved into the database. My problem is that whenever a user selects a file that exceeds a certain amoutn of Mb (about 8) I get a page error that says the following:
The connection was reset The connection to the server was reset while the page was loading.
I don't mind that there's an 8Mb limit to the files the users are uploading however I need to stop the current error from happening and display a proper validation message (preferably with the ModelState.AddModelError function). Can anybody help me? I can't seem to 'catch' the error before anything else happens in the page since it's happening before it arrives in the upload function within the controller.
-
William Calleja about 12 yearsFirst of all, thank you very much Darin. I am attempting to implement your solution however I cannot seem to be able to use 'IClientValidatable'. I have the System.Web.Mvc both added in the references of the project and in the page usings. What am I doing wrong?
-
Darin Dimitrov about 12 yearsI don't know what you are doing wrong. The
IClientValidatable
was added in ASP.NET MVC 3 in theSystem.Web.Mvc.dll
assembly inside theSystem.Web.Mvc
namespace. -
William Calleja about 12 yearsI verified and I was mistaken into thinking that we were using MVC3, we're in fact using MVC2. Since upgrading isn't an option for me, can any part of this solution still apply?
-
Darin Dimitrov about 12 years@WilliamCalleja, the server side validation solution still applies. As far as client side is concerned, ASP.NET MVC 3 uses the jQuery validate plugin alongside to perform unobtrusive client side validation. In ASP.NET MVC 1 and 2 the default client side validation framework is
MicrosoftMvcValidation.js
. It's a pretty different model. You may take a look at the following post: haacked.com/archive/2009/11/18/… -
William Calleja about 12 yearsIt worked! I wish I could give the answer to two of you but alas I can't, I combined ideas from Darin and Jenea, using the '</system.web> <location path="Media/Upload"> <system.web> <httpRuntime executionTimeout="600" maxRequestLength="10485760" /> </system.web> </location>'
-
MsBao about 10 yearsAwesome answer! There is one problem, though. MaxFileSizeAttribute.IsValid returns false when the value is null, effectively requiring a file to be uploaded.
-
Matt almost 10 yearsThere is one minor bug in your client side javascript validation. The last line should be "return element.files[0].size <= params.maxsize;" in the event they are uploading a file that is the exact size as your maximum. Otherwise this was very helpful.
-
Dominique Alexandre over 8 yearsTo prevent the required check, just type in "IsValid" : if (file == null) return true;