本文转载:http://www.cnblogs.com/TianFang/archive/2007/01/03/610739.html
断点续传的原理很简单,就是在Http的请求和应答的报文头上和一般的下载有所不同而已。 普通方式请求服务器上的一个文时,所发出的请求和接受到的服务器如下: request header: Cache-Control: no-cache Connection: close Pragma: no-cache Accept: */* Host: localhost response header: 200 Content-Type: application/octet-stream Content-Disposition: attachment;FileName=time.pdf 当服务器支持断点续传时,请求和应答如下: request header: Cache-Control: no-cache Connection: close Pragma: no-cache Accept: */* Host: localhost Range: bytes=15360- response header: 206 Content-Type: application/octet-stream Content-Disposition: attachment;FileName=time.pdf 两个报文的不同部分已用红色部分标记出来。可以看出: 客户端报文头中通过Range报文头来标识客户期望的下载位置。 服务器的应答号为200时表示是从文件头开始下载,而206表示是从文件的特定位置开始传输,客户端从该应答号可以看出服务器是否支持断点续传。 也就是说,支持断点续传的时候可以从文件任一部分开始下载,而普通的方式只能从文件头开始下载。 要使得服务器支持断点续传,需要解决以下几个问题: 1。需要判断客户端是否是续传请求,如果是续传请求时,需要获取客户端所需的文件范围。 从上面的分析可以看到,当客户端为断点传输时,报文头里会增加Range字段,则可以通过如下方式判断是否是断点传输请求。 string range = request.Headers["Range"]; bool isResume = string.IsNullOrEmpty(range); 2。对客户端做正确的应答相应,以通知客户端服务器支持端点续传 当为断点传输请求时,对客户端的相应号可以通过如下方式设置: response.StatusCode = 206; 3。传送客户端所需正确的内容 传送客户端所需正确的内容一般需要经过以下几个步骤 通过分析range来获取客户端的文件请求范围。 断点传输请求时,所需的长度比文件的长度短,故需要正确的设置response.ContentLength64属性。 正确传输所需的内容 代码示例: static void ProcessHttpClient(object obj) { HttpListenerContext context = obj as HttpListenerContext; HttpListenerRequest request = context.Request; HttpListenerResponse response = context.Response; FileStream fs = File.OpenRead(@"f:\123.pdf"); //待下载的文件 long startPos = 0; string range = request.Headers["Range"]; bool isResume = string.IsNullOrEmpty(range); if (isResume) //断点续传请求 { //格式bytes=9216- startPos = long.Parse(range.Split('=')[1].Split('-')[0]); response.StatusCode = 206; response.ContentLength64 = fs.Length - startPos; fs.Position = startPos; //设置传送的起始位置 } else { response.ContentLength64 = fs.Length; } Console.WriteLine("request header"); Console.WriteLine(request.Headers.ToString()); response.ContentType = "application/octet-stream"; string fileName = "time.pdf"; response.AddHeader("Content-Disposition", "attachment;FileName=" + fileName); Stream output = response.OutputStream; try { Console.WriteLine("response header"); Console.WriteLine(response.Headers.ToString()); CopyStream(fs, output); //文件传输 output.Close(); } catch (HttpListenerException e) //在未写完所有文件时,如果客户端关闭连接,会抛此异常 { Console.WriteLine(e.Message); //output.Close(); //如果执行此函数会抛异常在写入所有字节之前不能关闭流。 } } static void CopyStream(Stream orgStream, Stream desStream) { byte[] buffer = new byte[1024]; int read = 0; while ((read = orgStream.Read(buffer, 0, 1024)) > 0) { desStream.Write(buffer, 0, read); System.Threading.Thread.Sleep(1000); //模拟慢速设备 } }