利用RFC1867标准处理文件上传的两种方式:
1.一次性得到上传的数据,然后分析处理。
看了N多代码之后发现,目前无组件程序和一些COM组件都是使用Request.BinaryRead方法。一次性得到上传的数据,然后分析处理。这就是为什么上传大文件很慢的原因了,IIS超时不说,就算几百M文件上去了,分析处理也得一阵子。
2.一边接收文件,一边写硬盘。
了解了一下国外的商业组件,比较流行的有Power-Web,AspUpload,ActiveFile,ABCUpload,aspSmartUpload,SA-FileUp。其中比较优秀的是ASPUPLOAD和SA-FILE,他们号称可以处理2G的文件(SA-FILE EE版甚至没有文件大小的限制),而且效率也是非常棒,难道编程语言的效率差这么多?查了一些资料,觉得他们都是直接操作文件流。这样就不受文件大小的制约。但老外的东西也不是绝对完美,ASPUPLOAD处理大文件后,内存占用情况惊人。1G左右都是稀松平常。至于SA-FILE虽然是好东西但是破解难寻。然后发现2款.NET上传组件,Lion.Web.UpLoadModule和AspnetUpload也是操作文件流。但是上传速度和CPU占用率都不如老外的商业组件。
做了个测试,LAN内传1G的文件。ASPUPLOAD上传速度平均是4.4M/s,CPU占用10-15,内存占用700M。SA-FILE也差不多这样。而AspnetUpload最快也只有1.5M/s,平均是700K/s,CPU占用15-39,测试环境: PIII800,256M内存,100M LAN。我想AspnetUpload速度慢是可能因为一边接收文件,一边写硬盘。资源占用低的代价就是降低传输速度。但也不得不佩服老外的程序,CPU占用如此之低.....
三、ASP.NET上传文件遇到的问题
我们在用ASP.NET上传大文件时都遇到过这样或那样的问题。设置很大的maxRequestLength值并不能完全解决问题,因为ASP.NET会block直到把整个文件载入内存后,再加以处理。实际上,如果文件很大的话,我们经常会见到Internet Explorer显示 "The page cannot be displayed - Cannot find server or DNS Error",好像是怎么也catch不了这个错误。为什么?因为这是个client side错误,server side端的Application_Error是处理不到的。
四、ASP.NET大文件上传解决方案
解决的方法是利用隐含的HttpWorkerRequest,用它的GetPreloadedEntityBody 和 ReadEntityBody方法从IIS为ASP.NET建立的pipe里分块读取数据。Chris Hynes为我们提供了这样的一个方案(用HttpModule),该方案除了允许你上传大文件外,还能实时显示上传进度。
Lion.Web.UpLoadModule和AspnetUpload 两个.NET组件都是利用的这个方案。
方案原理:
利用HttpHandler实现了类似于ISAPI Extention的功能,处理请求(Request)的信息和发送响应(Response)。
方案要点:
1. httpHandler or HttpModule
a.在asp.net进程处理request请求之前截获request对象
b.分块读取和写入数据
c.实时跟踪上传进度更新meta信息
2. 利用隐含的HttpWorkerRequest用它的GetPreloadedEntityBody 和 ReadEntityBody方法处理文件流
=====================================================================
已经忘记上面的这段说明是从什么地方搜索到的了,它对ASP.net里的大文件上传说明的还是很清楚的。而我只想在这里说明一个问题,就是:
实际上,如果文件很大的话,我们经常会见到Internet Explorer显示 "The page cannot be displayed - Cannot find server or DNS Error",好像是怎么也catch不了这个错误。为什么?因为这是个client side错误,server side端的Application_Error是处理不到的。
我试了好多次,发现这个错误确实是在服务器端,这是我的证明:
11/7/2005 11:07:13 AM aa8b180d-a91e-4734-938f-00c066b4993b:aa8b180d-a91e-4734-938f-00c066b4993b.doc
11/7/2005 11:08:07 AM Start Upload.......
11/7/2005 11:08:11 AM HttpModule error.....
11/7/2005 11:09:31 AM Start Upload.......
11/7/2005 11:09:44 AM HttpModule error.....
11/7/2005 11:10:43 AM Start Upload.......
11/7/2005 11:10:46 AM 55007:49152
11/7/2005 11:10:46 AM -----------------------------7d531c2e1705ac
其中的两次,都是因为我上传了大的文件,而使程序出现了错误。而这个错误不是用try和catch块来处理的,而是在
private void WebbUpload_Error(object sender, EventArgs e)
{
#region function WebbUpload_Error
WebbTextTrace.TraceMsg("HttpModule error.....");
#endregion
this.Dispose();
}
也就是说,当我们上传很大的文件的时候,服务器它会很快的检测文件的大小,然后返回错误。事实也证明,每次我都只能上传一半的文件。出现这个问题是怎么回事呢?
如果我们没有自己写HttpModule,那么服务器有自己的处理模块,这个时候就是只能上传小文件,太大的文件服务器就会现出错误。
而如果我们自己写了HttpModule之后就是不是一定不现出这个错误呢?不一定,我就在测试中遇到好几次这样的问题,原因是我在处理文件流时候,或者根本就不是ASP.net的HttpModul出现问题,而是出现一些没有办法捕获到的异常(.net里有很多),确切的说是我们的.net平台上的托管代码没办法捕获到的异常,而这个异常只能交给IIS的非托管模块去捕获了。所以最后返回的就是服务器没有找到的错误。
因此,如果现出这样的错误,而且按照上面的办法写了自己的HttpModul模块还不能解决问题的,那就是说:你的模块里发生了.net不兼容的异常,而此异常导致了上面的错误。而出现.net无法捕获的异常,可以很肯定的说:一定是你的程序出现了问题。所以,这个时候注意,小心检测一下代码。。
我出现了好几次错误,分别是出现在这些地方:
1、一次字节循环读取错误。
2、一次除0错误(这个应该是可以捕获到的吧,但没有,不知道为什么?)
3、资源锁定错误。确切的说,我自己也记不清到底是什么错误,因为每次都只是一个服务器找不到的错误,之后只能通过手动的查找代码,发现问题,然后猜测。。。
.net本身的异常机制里就有一些问题,特别是在托管兼容上。
好了,希望这篇文章能帮助一些人。。。