升级版本:
Response.WriteFile使用起来很方便,但是当网站为浏览者提供大块头文件的下载服务时就会发现WriterFile简直就是恶梦,它会非常占用资源(以下是本人猜测,如果有不对的地方,请指正!) ,当你的快餐店来了一个胃口很大的客人,要了一百包薯条准备整个上午都在店里面看表格,恰好碰上一个死心眼的大厨,他总觉得自己应该在最快的时候内把所有的东西都做好,然后把它们完整的呈现在顾客的面前,结果呢!可想而知,那个顾客因为饥饿而晕倒在了自己的座位上!这就是我们今天要讲的内容,你的Server也许只有2G的内容,当然IIS的限制也正好在这个位置,但是如果同时有人发起了两个以上大文件的请求的时候,你的内存就会忙于装填那些将要发包出去的字节码,而这个动作可能会和其他千万个Action一起哄抢本来就不多的资源,有没有什么办法可以解决呢?我们来看看下面的方案(声明这个方案也不是我想出来的,出自MSDN Magazine,就是忘记哪一期了!):
int chunkSize = 1000;byte buffer = new byte[chunkSize];using (FileStream fs = fi.Open(FileMode.Open)){while (fs.Position >= 0 && Response.IsClientConnected){int tmp = fs.Read(buffer, 0, chunkSize);Response.OutputStream.Write(buffer, 0, tmp);Response.Flush();}}
代码很简单,就是用上面的代码替换掉Response.WriteFile方法,这样在内存中建立一个buffer的缓冲区(如果我的想法没有错的话,原理先放在一边,事实上这些代码确实起作用了!),然后去轮循字节信息,这样处理较第一种方式快很多输出1G的内容很快,但是没有进行具体测试,不知道会不会给CPU或是其他方面带来新的负载。而Response.IsClientConnected可以判断连接状态是否激活,就好比上面那个顾客只吃了50包,就撑倒了,那我们就需要把手头的事情放下,帮忙打个120。
下面的压缩包是一个IHttpHandler实现的App_Data目录内容浏览和文件下载的示例,还有很多缺陷,比如说没有针对权限作出甄别监测,当然需要只是简单的管理权限,那就在<location>节点里面配置一下也好,Web.config的配置代码如下:
<httpHandlers> <add verb="GET" path="adbrowser.o" type="I.HttpHandler.AppDataBrowser,I.Controls" /><add verb="*" path="download.o" type="I.HttpHandler.Download,I.Controls" /></httpHandlers>
点击下载:
/uploads/soft/1_080616081804.rar |
备选版本:
所谓高级版,其实又算是一个微软的私有定义了,使用TransmitFile可以分段输出,大家都知道IE支持断点续传的,但是有时候当我们下载一半中断之后,我们再去请求的时候,突然IE的普通下载就变成续传型了,很神奇,能碰到机会和出去逛街捡了一万块钱的几率相当。传说中在IE请求的时候会传入时会附加一个Header,叫做Range用来框定目前下载文件的长度,和已下载的字节位置,然后结合creation-date,modification-date去判断是否可以续连上一次下载的内容接着下载,但是这个又有一个新的麻烦的点,首先我用Reflector拆开TransmitFile看了一下,与WriteFile一样的实现机制,依然会有资源占有的问题,然后就是只针对IE,用Fiddler抓了一上午都没有发现FF或Opera之流,但是没有对迅雷或者是快车进行监测,不知道这