使用过ASP.NET的开发者都知道,FileUpload控件是一把双刃剑——既可能成为我们的救世主,也能变成我们的敌人。其中一个很常见的问题就是如何处理超过4MB的大文件上传。
使用FileUpload控件进行文件上传是一件非常有技巧性的事情。开发者应该了解的是,之所以默认的文件大小上限为4MB,并不是因为当时的设计人员灵光一现,而是为了避免潜在DOS攻击危险。
若是攻击者提交了一个或多个大文件,往往会让服务器不堪重负。若是用户上传的文件大于4MB,将会得到“Maximum request length exceeded.”异常信息。
想增加这个尺寸的上限并没有什么难度,不过开发者需要知道怎样做才是最好的方法。默认的4MB设定于系统的machine.config文件中,不过我们在web.config中即可覆盖该值。
例如,若想将上传文件的上限提高至20MB,我们只需要这样修改:
<system.web> <httpRuntime executionTimeout="240" maxRequestLength="20480" /> </system.web>
若是在machine.config中对该值进行了修改,那么同时受到影响的就不只是这一个网站。ASP.NET之所以设计了这样的上限,就是为了避免潜 在的攻击。所以最好的方式是在某个特定目录中进行覆盖,而不是整个应用程序。web.config文件的格式非常灵活,因此实现这样的需求也不难:
web.config文件允许级联覆盖,所以很容易就能够实现这个要求。我们可以在某个文件夹中添加web.config文件,并书写上述配置,或者干脆在web.config文件中添加一个专门的<location />标签,也能达到同样的效果:
<location path="Upload"> <system.web> <httpRuntime executionTimeout="110" maxRequestLength="20000" /> </system.web> </location>
对于允许大文件上传来说,改变默认的上传文件大小限制仅仅是我们要做的第一步。一篇名为文件上传的不为人知一面的文章揭示了更多有关与IIS配合完成文件上传的细节。
若是上传的文件太大的话,往往会出现一些很有意思的情况。无论
maxRequestLength
在中设置成什么,IIS都会不假思索地接受,但随后在ASP.NET检查时就会抛出异常。
这篇文章还提到:
当然很容易就可以捕获到这个异常,不过这并不是我们所期待的。还有一种方法是覆写Page.OnError方法,并通过检查在发生HttpException异常时HTTP响应代码是否为400来判断,不过这也不够完美。
给用户充分提示
一个让用户很反感的做法就是误导用户,而且在Web应用程序执行操作时也不给用户任何提示。文件尺寸的限制写在web.config中,所以将一段提示文字放在web.config中也就变成了件非常自然的事。
最好的做法就是在运行时读取web.config中的httpRuntime节,并转化为HttpRuntimeSection对象。非常简单:
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);
这个解决方案清晰易懂,也没有什么冗繁的代码。
围绕着ASP.NET中上传文件这个话题也讨论了不少了,还有什么没有涉及到的吗?个人认为其实至少还有一个非常重要问题是没有讨论过,那就是在处理上传文件时占用ASP.NET处理线程的问题。众所周知,ASP.NET处理请求时会用到线程池中的线程,当线程池中的线程被用完之后没有被处理的请求只能排队了。因此增大ASP.NET应用程序吞吐量的一个重要手段,就是为一些耗时的操作使用异步处理方式(事实上这一命题可以在大部分应用中成立)。例如一个数据库查询操作需要3秒钟,如果不使用异步操作,处理线程就会被阻塞,直至查询完成。如果使用异步方式来执行数据库查询,在这3秒钟内线程就可以用户处理其他请求,当异步操作结束之后,ASP.NET就会使用另一个线程来继续处理这个请求。
上传大文件也是一个长时间占用处理线程的工作,而且遗憾的是,这无法使用异步操作来完成(通过异步操作来释放处理线程需要操作系统的支持,因此只有少量功能可以使用异步操作)。如果一个文件上传需要3分钟时间,那么在这3分钟内就会独占一个处理线程,如果上传文件的连接一多,就会大大影响应用程序的性能——就像遭受了某种方式的DOS攻击一样。因此,即使使用了像NeatUpload和swfupload这样的组件,也无法解决上传连接过多造成可用线程减少的问题。要解决这个问题并不容易,以下是两种思路(欢迎大家就此问题进行讨论):
- 扩展IIS,使上传文件或处理文件的过程不经ASP.NET处理,以减少ASP.NET应用程序线程的消耗。现在有了IIS 7,如果使用集成管道模式,应该也可以使用托管代码进行扩展。
- 使用额外的ASP.NET应用程序处理文件上传,以节省上传文件的线程对原ASP.NET应用程序线程的消耗。
就先说到这里吧
围绕着ASP.NET中上传文件这个话题也讨论了不少了,还有什么没有涉及到的吗?个人认为其实至少还有一个非常重要问题是没有讨论过,那就是在处理上传文件时占用ASP.NET处理线程的问题。众所周知,ASP.NET处理请求时会用到线程池中的线程,当线程池中的线程被用完之后没有被处理的请求只能排队了。因此增大ASP.NET应用程序吞吐量的一个重要手段,就是为一些耗时的操作使用异步处理方式(事实上这一命题可以在大部分应用中成立)。例如一个数据库查询操作需要3秒钟,如果不使用异步操作,处理线程就会被阻塞,直至查询完成。如果使用异步方式来执行数据库查询,在这3秒钟内线程就可以用户处理其他请求,当异步操作结束之后,ASP.NET就会使用另一个线程来继续处理这个请求。
上传大文件也是一个长时间占用处理线程的工作,而且遗憾的是,这无法使用异步操作来完成(通过异步操作来释放处理线程需要操作系统的支持,因此只有少量功能可以使用异步操作)。如果一个文件上传需要3分钟时间,那么在这3分钟内就会独占一个处理线程,如果上传文件的连接一多,就会大大影响应用程序的性能——就像遭受了某种方式的DOS攻击一样。因此,即使使用了像NeatUpload和swfupload这样的组件,也无法解决上传连接过多造成可用线程减少的问题。要解决这个问题并不容易,以下是两种思路(欢迎大家就此问题进行讨论):
- 扩展IIS,使上传文件或处理文件的过程不经ASP.NET处理,以减少ASP.NET应用程序线程的消耗。现在有了IIS 7,如果使用集成管道模式,应该也可以使用托管代码进行扩展。
- 使用额外的ASP.NET应用程序处理文件上传,以节省上传文件的线程对原ASP.NET应用程序线程的消耗。
就先说到这里吧