上面的方式我们发现,如果下载到一个半断网了下次会重头开始下载。这和我们今天的主题明显不符嘛。下面我们开始正式进入主题文件下载之断点续传。把前面我们说到的头属性Range用起来。
var request = new HttpRequestMessage { RequestUri = new Uri(url) }; request.Headers.Range = new RangeHeaderValue(rangeBegin, null); //【关键点】全局变量记录已经下载了多少,然后下次从这个位置开始下载。 var httpResponseMessage = await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);完整代码:
/// <summary> /// 是否暂停 /// </summary> static bool isPause = true; /// <summary> /// 下载开始位置(也就是已经下载了的位置) /// </summary> static long rangeBegin = 0; //(当然,这个值也可以存为持久化。如文本、数据库等) private async void button3_ClickAsync(object sender, EventArgs e) { isPause = !isPause; if (!isPause)//点击下载 { button3.Text = "暂停"; await Task.Run(async () => { //异步操作UI元素 label1.Invoke((Action)(() => { label1.Text = "准备下载..."; })); long downloadSpeed = 0;//下载速度 using (HttpClient http = new HttpClient()) { var url = ":813/新建文件夹2.rar"; var request = new HttpRequestMessage { RequestUri = new Uri(url) }; request.Headers.Range = new RangeHeaderValue(rangeBegin, null); //【关键点】全局变量记录已经下载了多少,然后下次从这个位置开始下载。 var httpResponseMessage = await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); var contentLength = httpResponseMessage.Content.Headers.ContentLength;//本次请求的内容大小 if (httpResponseMessage.Content.Headers.ContentRange != null) //如果为空,则说明服务器不支持断点续传 { contentLength = httpResponseMessage.Content.Headers.ContentRange.Length;//服务器上的文件大小 } using (var stream = await httpResponseMessage.Content.ReadAsStreamAsync()) { var readLength = 1024000;//1000K byte[] bytes = new byte[readLength]; int writeLength; var beginSecond = DateTime.Now.Second;//当前时间秒 while ((writeLength = stream.Read(bytes, 0, readLength)) > 0 && !isPause) { //使用追加方式打开一个文件流 using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write)) { fs.Write(bytes, 0, writeLength); } downloadSpeed += writeLength; rangeBegin += writeLength; progressBar1.Invoke((Action)(() => { var endSecond = DateTime.Now.Second; if (beginSecond != endSecond)//计算速度 { downloadSpeed = downloadSpeed / (endSecond - beginSecond); label1.Text = "下载速度" + downloadSpeed / 1024 + "KB/S"; beginSecond = DateTime.Now.Second; downloadSpeed = 0;//清空 } progressBar1.Value = Math.Max((int)((rangeBegin) * 100 / contentLength), 1); })); } if (rangeBegin == contentLength) { label1.Invoke((Action)(() => { label1.Text = "下载完成"; })); } } } }); } else//点击暂停 { button3.Text = "继续下载"; label1.Text = "暂停下载"; } }效果图:
到现在为止,你以为我们的断点续传就完成了吗?
错,你有没有发现我们使用的下载链接是a标签的。也就是我们自己写服务端提供的下载链接是不是也可以支持断点续传呢?下面我换个下载链接试试便知。
测试结果如下:
发现并不支持断点续传。为什么a标签链接可以直接支持,我们写的下载却不支持呢。
a标签的链接指向的直接是iis上的文件(iis默认支持),而我们写的却没有做响应报文表头Range的处理。(没想象中的那么智能嘛 >_<)
前面我们说过,断线续传是HTTP的一个协议。我们遵守它,它就存在,我们不遵守它也就不存在。
那下面我们修改前面的文件下载代码(服务端):
然后再测试断点续传,完美支持。
多线程同时下载(分片下载)