Thursday, November 10, 2011

limiting file size of a file uploaded in asp.net


Large file uploads in ASP.NET

Uploading files via the FileUpload control gets tricky with big files. The default maximum filesize is 4MB - this is done to prevent denial of service attacks in which an attacker submitted one or more huge files which overwhelmed server resources. If a user uploads a file larger than 4MB, they'll get an error message: "Maximum request length exceeded."

Increasing the Maximum Upload Size

The 4MB default is set in machine.config, but you can override it in you web.config. For instance, to expand the upload limit to 20MB, you'd do this:
<system.web>
<httpRuntime executionTimeout="240" maxRequestLength="20480" />
</system.web>
Since the maximum request size limit is there to protect your site, it's best to expand the file-size limit for specific directories rather than your entire application. That's possible since the web.config allows for cascading overrides. You can add a web.config file to your folder which just contains the above, or you can use the <location> tag in your main web.config to achieve the same effect:
<location path="Upload">
<system.web>
<httpRuntime executionTimeout="110" maxRequestLength="20000" />
</system.web>
</location>

What Happens When I Upload A File That's Too Big?

While expanding the upload restriction is a start, it's not a full solution for large file uploads. Milan explains one of the biggest problems with large file uploads in The Dark Side Of File Uploads:
It gets really interesting if someone uploads a file that is too large. Regardless of what your maxRequestLength setting mandates, IIS has to guzzle it, and then ASP.NET checks its size against your size limit. At this point it throws an exception.
As Milan explains, you can trap the exception, but it's trickier than you'd expect. He talks about overriding Page.OnError and checking for HTTP error code 400 when the error is HttpException, which as he says is less than ideal.

At Least Give Me A Warning

If we've got a set limit on file upload sizes, we should at least tell our users what it is. Since this is a configurable value which we may change later, the best is to make our file size warning read directly from web.config setting. The best way to do this is to pull back the httpRuntime section as a HttpRuntimeSection object, which isn't too hard given:
System.Configuration.Configuration config = WebConfigurationManager.OpenWebConfiguration("~");
HttpRuntimeSection section = config.GetSection("system.web/httpRuntime") as HttpRuntimeSection;
double maxFileSize = Math.Round(section.MaxRequestLength / 1024.0, 1);
FileSizeLimit.Text = string.Format("Make sure your file is under {0:0.#} MB.", maxFileSize);

A Real Solution: an HttpModule to Handle File Uploads

There are better solutions to handling large file uploads in ASP.NET. A custom HttpHandler can provide a better user experience by displaying upload progress and allowing you to handle a file size problem in a more controlled fashion. Here's a summary from a cursory search:
One of the longest running threads on the ASP.NET Forums (going back 5 years): HttpHandler or HttpModule for file upload, large files, progress indicator?

A Better Solution: a RIA upload component

The project which caused me to look into this, Video.Show, was based on ASP.NET and Silverlight 1.0, so we weren't able to take advantage of RIA platforms which would support more advance upload handling. In most cases, though, I'd recommend replacing the FileUpload component with a Silverlight or Flash based file upload control. In addition to a better upload experience, these controls generally look better than the the generic button displayed for the <input type="file"> element which is rendered by the FileUpload control. The input / file element doesn't allow for CSS formatting, although smart CSS hackers always seem to find a way around these things.
Although there doesn't seem to be a project / component to support Silverlight uploads yet, Liquid Boy has a nice sample of a Silverlight 1.1 (oops, 2.0) based upload control. I've heard good things about SWFUpload, a Flash and JavaScript based upload system. Developers are responsible for handling a few JavaScript events as well as accepting the file on the server. That can be done pretty easily, as you can see from this ASP.NET sample implementation. Here's a screenshot from one of the SWFUpload online demos:
SWFUpload

What About IIS7?

Oh, and there's more thing to worry about. IIS7 has a built-in request scanning which imposes an upload file cap which defaults to 30MB. Again, this is a good feature, but it gets in the way if you're looking to upload files larger than 30MB. Steve Schofield posted about how to change this from the comandline:
appcmd set config "My Site/MyApp" -section:requestFiltering -requestLimits.maxAllowedContentLength:104857600 -commitpath:apphost

Why is this such a pain?

Browsers, and HTML in general, were never designed to handle large uploads gracefully. Jeff Atwood and I discussed this back in September, and he summed up the issue pretty well in his post asking Why Are Web Uploads So Painful? It's disappointing that browser standards have failed us to the point that we need to use browser extension technologies like Flash and Silverlight as a band-aid here. Browsers should support uploads over both HTTP and FTP (it is the file transfer protocol, after all), and should manage the upload in a side or toolbar that allows us to continue browsing without breaking the upload. The Firefox Universal Upload add-on is an example of how this should work out of the box.
 
Historical trivia: In ASP.NET 1.0 and 1.1, the entire file was loaded into memory before being written to disk. There were improvements in ASP.NET 2.0 to stream the file to disk during the upload process.