在ASP.NET中跟踪和恢复大文件下载
核心提示:在ASP.NET中跟踪和恢复大文件下载,浏览在ASP.NET中跟踪和恢复大文件下载,文/陶刚在Web利用程序中处理大文件下载的题目1直出了名的困难,因此对大多数站点来讲,假设用户的下载被中断了,它们只能说悲痛降临到用户的身上了。但是我们现在没必要这样了,由于你可使自己
在Web利用程序中处理大文件下载的题目1直出了名的困难,因此对大多数站点来讲,假设用户的下载被中断了,它们只能说悲痛降临到用户的身上了。但是我们现在没必要这样了,由于你可使自己的ASP.NET利用程序有能力支持可恢复(继续)的大文件下载。使用本文提供的方法的时候,你可以跟踪下载的进程,这样你便可以够处理动态建立的文件--而且要到达这个目标根本不需要旧式的ISAPI动态链接库和非受控的(unmanaged)C++代码。
为客户端提供从互联网上下载文件的服务最轻易了,对吗?仅仅只需要把可下载的文件复制到你的Web利用程序目录中,发布链接并让IIS完成所有相干的工作。但是,文件服务不应当比脖子上的疼痛还要多(还要麻烦),你不希看全部世界都能访问自己的数据,你不希看服务器被数百个静态文件塞满了,你乃至于希看下载临时文件--只有当客户端开始下载后的空闲时间才建立这些文件。
不幸的是,使用IIS对下载要求的默许的响应是不可能到达这些效果的。因此在1般情况下,为了取得对下载进程的控制权,开发者需要链接到1个定制的.aspx页面,在这个页面中它们检查用户凭证(credential)、建立可以下载的文件并使用下面的代码把该文件推送给客户端:
Response.WriteFile
Response.End()
而这就是出现真正麻烦的地方。
有甚么题目?
WriteFile方法看起来非常完善,它使文件的2进制数据流向客户端。但是直到最近我们才知道,WriteFile方法是1个出名的内存占用狂,它把全部文件载进服务器的RAM中来提供服务(实际上它乃至于会占用文件两倍大小的空间)。对大文件,这会引发服务内存题目,并且可能重复ASP.NET进程。但是在2004年6月微软发布了1个补钉解决了这个题目。这个补钉现在是.NETFramework1.1补钉包(SP1)的1部份。
这个补钉引进了TransmitFile方法,它把1个磁盘文件读进到较小的内存缓冲区以后就开始传输该文件。虽然这个方案解决了内存和循环的题目,但是它依然不能使人满足。你不能控制响应的生命周期。你没法知道下载是否是正确地完成了,你没有办法知道下载是否是被中断了,并且(假设你建立了临时文件)你也不知道是否是应当、和甚么时候可以删除这些文件。更糟的是,假设下载的确失败了,TransmitFile方法又从客户端下次尝试的文件头部开始下载。
其中1种可能的解决方案--实现后台智能传输服务(BITS)对多数站点来讲是不可行的,由于这会毁掉保持客户端浏览器和操纵系***立性而作出的努力。
使人满足的解决方案的基础还是来自微软用于解决WriteFile引发的内存混乱题目的第1次尝试(见知识库文章812406)。那篇文章演示了智能的大块数据下载进程,它从文件流中读取数据。在服务器把字节块发送给客户端之前,它使用Response.IsClientConnected属性检查客户端是否是依然保持着连接。假设依然保持连接,它就继续发送流字节,否则就停止,以避免服务器发送没必要要的数据。
这就是我们采取的方法,特别是在下载临时文件的时候。在IsClientConnected返回False的情况下,你就知道下载进程被中断了,你应当保存文件;反之,当这个进程成功完成的时候,你就删除临时文件。另外,为了恢复中断了的下载,你需要做的工作是从上次下载尝试进程中客户端连接失败的文件点开始下载。
1 2 3 4 5 6 下1页
核心提示:在ASP.NET中跟踪和恢复大文件下载,浏览在ASP.NET中跟踪和恢复大文件下载,文/陶刚在Web利用程序中处理大文件下载的题目1直出了名的困难,因此对大多数站点来讲,假设用户的下载被中断了,它们只能说悲痛降临到用户的身上了。但是我们现在没必要这样了,由于你可使自己
HTTP协议和头信息(Header)支持
HTTP协议支持可以用于处理被中断下载的头信息。使用少许的HTTP头信息,你可以增强自己的下载进程,使它完全遵守HTTP协议规范。这个规范与ranges1起提供恢复被中断的下载所需要的1切信息。
下面是它的工作方式。首先,假设服务器支持客户端断点续传,它就在初始的响应中发送Accept-Ranges头信息。服务器还发送1个实体标签(entitytag)头信息(ETag),它包括1个唯1的标识字符串。
下面的代码显示了IIS发送给客户真个用于响应1个初始下载要求的1些头信息,它向客户端传递了被要求的文件的具体信息。
HTTP/1.1200OK
Connection:close
Date:Tue,19Oct200415:11:23GMT
Accept-Ranges:bytes
Last-Modified:Sun,26Sep200415:52:45GMT
ETag:"47febb2cfd76c41:2062"
Cache-Control:private
Content-Type:application/x-zip-compressed
Content-Length:2844011
在接收这些头信息以后,假设下载被中断了,IE浏览器在后来的下载要求中会把Etag值和Range头信息发送回服务器。下面的代码显示了尝试恢复被中断下载时IE发送给服务器的1些头信息。
GEThttp://192.168.100.100/download.zipHTTP/1.0
Range:bytes=822603-
Unless-Modified-Since:Sun,26Sep200415:52:45GMT
If-Range:"47febb2cfd76c41:2062"
这些头信息表明IE缓存了IIS提供的实体标签,并在If-Range头信息中把它发送回服务器了,这是确保下载从正确相同的文件恢复的1种途径。不幸的是,并非所有的浏览器的工作方式都相同。客户端发送的用于验证文件的其它HTTP头信息多是If-Match、If-Unmodified-Since或Unless-Modified-Since。很明显,该规范对客户端软件必须支持哪些头信息,或必须使用哪些头信息没有明确的规定。因此,有些客户端根本就没有使用头信息,而IE只使用If-Range和Unless-Modified-Since。你最好用代码检查这些信息。采取这类方式的时候,你的利用程序可以在非常高的层次遵守HTTP规范,并可使用多种浏览器。Range头信息指明了被要求的字节范围--在例子中它是服务器应当恢复文件流的起始点。
当IIS接收到恢复下载的要求类型时,它发回包括下面的头信息的响应信息:
HTTP/1.1206PartialContent
Content-Range:bytes822603⑵844010/2844011
Accept-Ranges:bytes
Last-Modified:Sun,26Sep200415:52:45GMT
ETag:"47febb2cfd76c41:2062"
Cache-Control:private
Content-Type:application/x-zip-compressed
Content-Length:2021408
请留意上面的代码与最初的下载要求的HTTP响应有点差别--恢复下载的要求是206而最初下载的要求是200。这表明通过线路传递进来的内容是部份文件。这1次Content-Range头信息指出了被传递字节的精确数目和位置。
IE对这些头信息是很挑剔的。假设最初的响应没有包括Etag头信息,IE永久不会尝试恢复下载。我测试过的其它客户端不使用ETag头信息,它们简单得依托于文件名、要求范围,并使用Last-Modified头信息(假设它们试图验证该文件)。
上1页 1 2 3 4 5 6 下1页
核心提示:在ASP.NET中跟踪和恢复大文件下载,浏览在ASP.NET中跟踪和恢复大文件下载,文/陶刚在Web利用程序中处理大文件下载的题目1直出了名的困难,因此对大多数站点来讲,假设用户的下载被中断了,它们只能说悲痛降临到用户的身上了。但是我们现在没必要这样了,由于你可使自己
深进了解HTTP协议
前面的部份中显示的头信息对使恢复下载的解决方案运行来讲是足够的,但是它没有完全覆盖HTTP规范。
在单个要求中,Range头信息可以询问多个范围,这类特性称为"多部份范围(multipartranges)"。请不要与分段下载(segmenteddownloading)混淆,几近所有的下载工具都使用分段下载来进步下载速度。这些工具宣称通过打开两个或多个并发的连接(每个连接要求文件的不同范围)进步了下载速度。
多部份范围的想法并没有开启多个连接,但是它可使客户端软件可以在单个要求/响应周期中要求某个文件的最前面的10个和最后面的10个字节。
老实地说,我历来都没有找到使用这类特性软件片断。但是我拒尽在代码声明中写进"它其实不是完全的HTTP兼容的"。略往这个特性1定会触犯墨菲法则(Murphy'sLaw)。不管如何,多部份范围还是被用于电子邮件传输中,把头信息、普通文本和附件分开。
示例代码
我们知道了客户端和服务器如何交换头信息以保证可恢复的下载,把这些知识与文件块流的思想结合起来,你便可以够给自己的ASP.NET利用程序增加可靠的下载治理能力了。
获得下载进程的控制权的方法是从客户端截取下载要求、读取头信息并适当地响应。在.NET之前,你必须编写ISAPI(Internet服务器API)利用程序来实现这类功能,但是.NET框架组件提供了1个IHttpHandler接口,在类中实现的时候,它答应你仅仅使用.NET代码便可以够截取和处理要求。这意味着你的利用程序对下载进程有完全控制权和响应性,不再会触及或使用IIS的自动化函数。
示例代码在HttpHandler.vb文件中包括了1个自定义的HttpHandler类(ZIPHandler)。ZipHandler实现了IhttpHandler接口,并且处理对所有.zip文件的要求。
为了测试示例代码,你需要在IIS中建立1个新的虚拟目录,并把源文件复制到那儿。在该目录中建立1个叫做download.zip的文件(请留意IIS和ASP.NET不能处理大于2GB的下载,因此要确保你的文件没有超过该限制)。配置你的IIS虚拟目录,通过aspnet_isapi.dll映照.zip扩大名。
上1页 1 2 3 4 5 6 下1页
核心提示:在ASP.NET中跟踪和恢复大文件下载,浏览在ASP.NET中跟踪和恢复大文件下载,文/陶刚在Web利用程序中处理大文件下载的题目1直出了名的困难,因此对大多数站点来讲,假设用户的下载被中断了,它们只能说悲痛降临到用户的身上了。但是我们现在没必要这样了,由于你可使自己
HttpHandler类:ZIPHandler
在ASP.NET中映照了.zip扩大名以后,客户端每次向服务器要求.zip文件的时候,IIS调用ZipHandler类的ProcessRequest方法(见下载代码)。
ProcessRequest方法首先建立自定义的FileInformation类(见下载代码)的1个实例,它封装了下载的状态(例如进行中、被中断了等等)。示例把download.zip示例文件的路径硬编码到代码中了。假设把这段代码利用于你自己的利用程序,需要修改它来打开被要求的文件。
'使用objRequest检测要求了哪个文件,用该文件打开objFile。
'例如objFile=NewDownload.FileInformation(<完全文件名>)
objFile=NewDownload.FileInformation(_
objContext.Server.MapPath("~/download.zip"))
接下来,程序使用描写的HTTP头信息(假设要求提供了头信息)执幸幌盗械难橹ぜ觳椤K?衙恐旨觳槎挤庾霸谛⌒退接泻??校?绻?橹こ晒Φ幕熬头祷豑rue。假设某个验证检查失败了,响应会立即终止,并发送适当的StatusCode值。
IfNotobjRequest.HttpMethod.Equals(HTTP_METHOD_GET)OrNot
objRequest.HttpMethod.Equals(HTTP_METHOD_HEAD)Then
'目前只支持GET和HEAD方法
objResponse.StatusCode=501'没有履行
ElseIfNotobjFile.ExistsThen
'没法找到被要求的文件
objResponse.StatusCode=404'没有找到
ElseIfobjFile.Length>Int32.MaxValueThen
'文件太大了
objResponse.StatusCode=413'要求实体太大
ElseIfNotParseRequestHeaderRange(objRequest,alRequestedRangesBegin,alRequestedRangesend,_
objFile.Length,bIsRangeRequest)Then
'Range要求中包括无用的实体
objResponse.StatusCode=400'无用的要求
ElseIfNotCheckIfModifiedSince(objRequest,objFile)Then
'实体没有被修改过
objResponse.StatusCode=304'没有被修改过
ElseIfNotCheckIfUnmodifiedSince(objRequest,objFile)Then
'实体在上次被要求的日期以后被修改过
objResponse.StatusCode=412'预处理失败
ElseIfNotCheckIfMatch(objRequest,objFile)Then
'实体与要求不匹配
objResponse.StatusCode=412'预处理失败
ElseIfNotCheckIfNoneMatch(objRequest,objResponse,objFile)Then
'实体的确与none-match要求匹配。
'响应代码位于CheckIfNoneMatch函数中
Else
'初步检查成功
这些初步检查的函数中的ParseRequestHeaderRange(见下载代码)检查客户端是否是要求了文件范围(这意味着是1个局部下载)。假设被要求的范围是无效的(无效范围指超出文件大小或包括不公道数字的范围数值),该方法把bIsRangeRequest设置为True。假设要求了范围,CheckIfRange方法会验证IfRange头信息。
上1页 1 2 3 4 5 6 下1页
核心提示:在ASP.NET中跟踪和恢复大文件下载,浏览在ASP.NET中跟踪和恢复大文件下载,文/陶刚在Web利用程序中处理大文件下载的题目1直出了名的困难,因此对大多数站点来讲,假设用户的下载被中断了,它们只能说悲痛降临到用户的身上了。但是我们现在没必要这样了,由于你可使自己
假设被要求的范围是有效的,代码会计算响应信息的大小。假设客户端要求了多个范围,响应信息大小的数值会包括多部份头部信息长度的数值。
假设不能肯定某个发送的头部信息值,程序将把这个下载要求作为最初要求而不是部份下载来处理,从文件的顶部开始发送1个新的下载流。
IfbIsRangeRequestAndAlsoCheckIfRange(objRequest,objFile)Then
'这是范围要求
'假设Range数组包括多个实体,它还是1个多部份范围要求
bMultipart=CBool(alRequestedRangesBegin.GetUpperBound(0)>0)
'进进每个范围来获得全部响应长度
ForiLoop=alRequestedRangesBegin.GetLowerBound(0)ToalRequestedRangesBegin.GetUpperBound(0)
'内容的长度(这个范围的)
iResponseContentLength+=Convert.ToInt32(alRequestedRangesend(_
iLoop)-alRequestedRangesBegin(iLoop))+1
IfbMultipartThen
'假设是多部份范围要求,计算出将发送的中间头信息的长度
iResponseContentLength+=MULTIPART_BOUNDARY.Length
iResponseContentLength+=objFile.ContentType.Length
iResponseContentLength+=alRequestedRangesBegin(iLoop).ToString.Length
iResponseContentLength+=alRequestedRangesend(iLoop).ToString.Length
iResponseContentLength+=objFile.Length.ToString.Length
'49是多部份下载中换行和其它必要的字符的长度
iResponseContentLength+=49
EndIf
NextiLoop
IfbMultipartThen
'假设是多部份范围要求,
'我们还必须计算出将发送的最后1个中间头信息的长度
iResponseContentLength+=MULTIPART_BOUNDARY.Length
'8是破折号和换行符的长度
iResponseContentLength+=8
Else
'不是多部份下载,因此我们必须说明初始HTTP头信息的响应范围
objResponse.AppendHeader(HTTP_HEADER_CONTENT_RANGE,"bytes"&_
alRequestedRangesBegin(0).ToString&"-"&_
alRequestedRangesend(0).ToString&"/"&_
objFile.Length.ToString)
'EndIf
'范围响应
objResponse.StatusCode=206'局部响应
Else
'这不是范围要求,或被要求的范围实体ID与当前的实体ID不匹配,
'因此开始新的下载
'指明文件完成部份的大小即是内容的长度
iResponseContentLength=Convert.ToInt32(objFile.Length)
'返回正常的OK状态
objResponse.StatusCode=200
EndIf
'接下来服务器必须发送几个重要的响应头信息,例如内容长度、Etag、和文件的内容类型:
'把内容长度写进响应
objResponse.AppendHeader(HTTP_HEADER_CONTENT_LENGTH,iResponseContentLength.ToString)
'把最后修改日期写进响应
objResponse.AppendHeader(HTTP_HEADER_LAST_MODIFIED,objFile.LastWriteTimeUTC.ToString("r"))
'告知客户端软件我们接受了范围要求
objResponse.AppendHeader(HTTP_HEADER_ACCEPT_RANGES,HTTP_HEADER_ACCEPT_RANGES_BYTES)
'把文件的实体标签写进响应(用引号括起来)
objResponse.AppendHeader(HTTP_HEADER_ENTITY_TAG,""""&objFile.EntityTag&"""")
'把内容类型写进响应
IfbMultipartThen
'多部份消息有这类特殊的类型
'在例子中文件实际的mime类型在以后才写进响应
objResponse.ContentType=MULTIPART_CONTENTTYPE
Else
'单个部份消息具有的文件内容类型
objResponse.ContentType=objFile.ContentType
EndIf
下载所需要的1切都豫备好了,可以开始下载文件了。你将使用FileStream对象从文件中读取字节块。把FileInformation实例objFile的State属性设置为fsDownloadInProgress。只要客户端保持连接,服务器就从文件中读取字节块并发送给客户端。对多部份下载,这段代码会发送特定的头信息。假设客户端中断连接,服务器就把文件状态设置为fsDownloadBroken。假设服务器完成了被要求范围的发送进程,它会把状态设置为fsDownloadFinished(见下载代码)。
上1页 1 2 3 4 5 6 下1页
核心提示:在ASP.NET中跟踪和恢复大文件下载,浏览在ASP.NET中跟踪和恢复大文件下载,文/陶刚在Web利用程序中处理大文件下载的题目1直出了名的困难,因此对大多数站点来讲,假设用户的下载被中断了,它们只能说悲痛降临到用户的身上了。但是我们现在没必要这样了,由于你可使自己
FileInformation辅助类
在ZIPHandler部份中你会发现,FileInformation是1个辅助类,它封装了下载状态信息(例以下载中、中断等等)。
为了建立FileInformation的实例,你需要把被要求文件的路径传递给该类的构造函数:
PublicSubNew(ByValsPathAsString)
m_objFile=NewSystem.IO.FileInfo(sPath)
EndSub
FileInformation使用System.IO.FileInfo对象来获得文件的信息,这些信息是作为该对象的属性暴露的(例如文件是否是存在、文件全名、大小等等)。这个类还暴露了1个DownloadState枚举,它描写了下载要求的多种状态:
EnumDownloadState
'Clear:没有下载进程,文件可能在保护
fsClear=1
'Locked:动态建立的文件不能被更改
fsLocked=2
'InProgress:文件被锁定了,下载进程正在进行
fsDownloadInProgress=6
'Broken:文件被锁定了,下载进程正在进行,但是被取消了
fsDownloadBroken=10
'Finished:文件被锁定了,下载进程完成了
fsDownloadFinished=18
EndEnum
FileInformation还提供了EntityTag属性值。示例代码中的这个值是硬编码的,这是由于示例代码只使用了1个下载文件,并且该文件不会被改变,但是对实际利用程序来讲,你会提供多个文件,乃至于动态地建立文件,你的代码必须为每个文件提供1个唯1的EntityTag值。另外,每次改变或修改该文件的时候,这个值也必须改变。这使客户端软件能够验证它们已下载的字节块是否是依然是最新的。下面是示例代码中返回硬编码EntityTag值的部份:
PublicReadOnlyPropertyEntityTag()AsString
'EntityTag用于对客户真个初始(200)响应,和来自客户真个恢复要求
Get
'为文件建立唯1的字符串。
'留意,只要文件没有产生改变,该唯1码就必须保存。
'但是,假设文件的确改变了或被修改了,这个码必须改变。
Return"MyExampleFileID"
EndGet
EndProperty
1个简单的和大致足够安全的EntityTag可能由文件名和文件最后被修改的日期组成。不管使用甚么方法,你都必须确保这个值是真的是唯1的,不会与其它文件的EntityTag混淆。我希看在自己的利用程序中依照客户、顾客和邮编索引来动态地替被建立的文件命名,并把用作EntityTag的GUID存储在数据库中。
ZipFileHandler类读取和设置公共的State属性。在完成下载以后,它把State设置为fsDownloadFinished。这个时候你便可以够删除临时文件了。这儿1般需要调用Save方法来保持状态。
PublicPropertyState()AsDownloadState
Get
Returnm_nState
EndGet
Set(ByValnStateAsDownloadState)
m_nState=nState
'可选操纵:这个时候你可以自动地删除文件。
'假设状态被设置为Finished,你就不再需要这个文件了。
'IfnState=DownloadState.fsDownloadFinishedThen
'Clear()
'Else
'Save()
'EndIf
Save()
EndSet
EndProperty
在文件状态产生改变的任甚么时候候ZipFileHandler都应当调用Save方法,保存文件的状态,这样在以后才能显示给用户。你还可以用它来保存你自己建立的EntityTag。请不要把文件的状态和EntityTag值保存在Application、Session或Cache中--你必须逾越所有的这些这些对象的生命周期来保存信息。
PrivateSubSave()
'把该文件下载的状态保存到数据库或XML文件中。
'固然,假设你并没有动态地建立文件,就不需要保存这个状态。
EndSub
前面提到,示例代码只处理1个已有的文件(download.zip),但是你可以进1步增强这个程序,根据需要建立被要求的文件。
测试示例代码的时候,你的本地系统或LAN可能太快了,以致于没法中断下载进程,因此我推荐你使用慢速LAN连接(在IIS中减少站点的带宽是1种摹拟的方法)或把服务器放到互联网上。
在客户端上下载文件依然很艰巨。ISP操纵的不对的或配置毛病的Web缓冲服务器都可能使大文件下载进程失败,包括下载状态恶化或早期对话终结。假设文件大小超过了255MB,你就应当鼓励顾客使用第3方下载治理软件,虽然某些最新的浏览器内建了基本的下载治理器。
假设你希看进1步扩大示例代码,查阅1下HTTP规范是有益的。你可以为下载建立MD5校验值,使用Content-MD5头信息添加它们,提供1种验证下载文件完全性的途径。示例代码除GET和HEAD之外没有触及到其它的HTTP方法。
上1页 1 2 3 4 5 6 唐山网站建设www.fw8.netTAG:代码,文件,信息,部份,客户端
评论加载中...
|