【转】文件下载之断点续传(客户端与服务端的落成)

眼前讲了文件的上传,前天来聊聊文件的下载。

前面讲了文本的上传,后天来聊天文件的下载。

惯例,依旧从最简易凶暴的发端。那么多简单算轻易?多狂暴算严酷?笔者报告你能够不写一句代码,你信呢?直接把3个文书往IIS服务器上1扔,就帮助下载。还TM么能够断点续传(IIS服务端私下认可帮忙)。

常规,照旧从最简便易行无情的发端。那么多轻易算简单?多凶残算严酷?小编告诉你能够不写一句代码,你信呢?直接把1个文本往IIS服务器上1扔,就扶助下载。还TM么能够断点续传(IIS服务端暗中认可扶助)。

在贴代码在此之前先来询问下哪些是断点续传(此地说的是下载断点续传)?怎么落到实处的断点续传?
断点续传正是下载了大意上断网可能暂停了,然后可以接着下载。不用从头起首下载。

在贴代码以前先来打探下怎样是断点续传(此间说的是下载断点续传)?怎么落实的断点续传?
断点续传正是下载了4/八断网可能暂停了,然后能够随着下载。不用从头起头下载。

很奇妙啊,其实轻巧得很,大家寻思也是能够想到的。
率先客户端向服务端发送三个伸手(下载文件)。然后服务端响应请求,消息包罗文件总大小、文件流开首和终结地点、内容大小等。那现实是怎么得以实现的呢?
HTTP/1.一有身材属性Range。比方您发送请求的时候带上Range:0-199,等于你是请求0到19玖里头的数目。然后服务器响应请求Content-Range: bytes 0-199/250
,表示你获取了0到19玖期间的多寡,总大小是250。(也正是报告您还会有数量未有下载完)。

很奇妙吗,其实轻松得很,大家寻思也是能够想到的。
第一客户端向服务端发送三个伸手(下载文件)。然后服务端响应请求,消息包括文件总大小、文件流初阶和终结地方、内容大小等。那现实是怎么落到实处的吧?
HTTP/一.一有身形属性Range。举例您发送请求的时候带上Range:0-199,等于你是请求0到19玖里头的多寡。然后服务器响应请求Content-Range: bytes 0-199/250
,表示你获得了0到199中间的数码,总大小是250。(也便是告诉你还会有数量没有下载完)。

大家来画个图吧。
韦德手机客户端 1

咱俩来画个图吧。
韦德手机客户端 1

是或不是比较粗略?这么神奇的事物相当于个“约定”而已,也便是所谓的HTTP协议。
但是,协议那东西你遵循它就存在,不遵从它就不设有。就如民国的钱我们都信它,它就有用。假设大多数人不信它,也就没卵用了。
以此断点续传也是如此。你服务端服从就援救,不听从也就不帮助断点续传。所以我们写下载工具的时候要求看清响应报文里有未有Content-Range,来确定是或不是支持断点续传。
废话够多了,下边撸起袖子开干。

是否很简短?这么美妙的事物也正是个“约定”而已,也正是所谓的HTTP协议。
可是,协议那东西你坚守它就存在,不信守它就不存在。仿佛中华民国时代的钱我们都信它,它就有用。假使超过一半人不信它,也就没卵用了。
以此断点续传也是如此。你服务端遵从就扶助,不遵循也就不帮忙断点续传。所以大家写下载工具的时候要求判别响应报文里有未有Content-Range,来显明是不是协助断点续传。
废话够多了,上面撸起袖子开干。

文件下载-服务端

文本下载-服务端

选取a标签提供文件下载

采纳a标签来下载文件,也便是大家日前说的不写代码即可达成下载。直接把公文往iis服务器上1扔,然后把链接贴到a标签上,完事。

<a href="/新建文件夹2.rar">下载</a>

轻巧易行、狂暴不用说了。如真得这么好那我们也不会讨厌去写其余下载逻辑了。这里有个沉重的毛病。这种方法提供的下载相当不够安全。哪个人都得以下载,未有权限调节,说不定还有也许会被人文件扫描(好像csdn就出过那档子事)。

选拔a标签提供文件下载

选取a标签来下载文件,也正是大家眼下说的不写代码就足以兑现下载。直接把公文往iis服务器上一扔,然后把链接贴到a标签上,完事。

<a href="/新建文件夹2.rar">下载</a>

简单易行、狂暴不用说了。如真得这么好那我们也不会讨厌去写其余下载逻辑了。这里有个致命的老毛病。这种方法提供的下载非常不够安全。哪个人都能够下载,未有权力决定,说不定还有只怕会被人文件扫描(好像csdn就出过那档子事)。

运用Response.TransmitFile提供文件下载

地点说直接a标签提供下载远远不够安全。那我们怎么提供相对安全的下载呢。asp.net私下认可App_Data文件夹是不能够被直接待上访问的,那大家把下载文件放那之中。然后下载的时候大家读取文件在回到到响应流。

//文件下载
public void FileDownload5()
{          
    //前面可以做用户登录验证、用户权限验证等。

    string filename = "大数据.rar";   //客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径 

    Response.ContentType = "application/octet-stream";  //二进制流
    Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
    Response.TransmitFile(filePath); //将指定文件写入 HTTP 响应输出流
}

应用Response.TransmitFile提供文件下载

地点说直接a标签提供下载远远不足安全。那我们怎么提供相对安全的下载呢。asp.net暗许App_Data文本夹是不能够被一向访问的,那我们把下载文件放那在这之中。然后下载的时候大家读取文件在回到到响应流。

//文件下载
public void FileDownload5()
{          
    //前面可以做用户登录验证、用户权限验证等。

    string filename = "大数据.rar";   //客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径 

    Response.ContentType = "application/octet-stream";  //二进制流
    Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
    Response.TransmitFile(filePath); //将指定文件写入 HTTP 响应输出流
}

其余办法文件下载

在英特网查找C#文本下载诚如都会搜到所谓的“三种方法”。其实这个代码并不可能拿来一贯动用,有坑的。
第一种:(Response.BinaryWrite)

 public void FileDownload2()
 {
     string fileName = "新建文件夹2.rar";//客户端保存的文件名  
     string filePath = Server.MapPath("/App_Data/新建文件夹2.rar");//要被下载的文件路径   

     Response.ContentType = "application/octet-stream";//二进制流
     //通知浏览器下载文件而不是打开  
     Response.AddHeader("Content-Disposition", "attachment;  filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));

     //以字符流的形式下载文件  
     using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
     {
         Response.AddHeader("Content-Length", fs.Length.ToString());
         //这里容易内存溢出
         //理论上数组最大长度 int.MaxValue 2147483647 
         //(实际分不到这么多,不同的程序能分到值也不同,本人机器,winfrom( 2147483591 相差56)、iis(也差不多2G)、iis Express(只有100多MB))
         byte[] bytes = new byte[(int)fs.Length];
         fs.Read(bytes, 0, bytes.Length);
         Response.BinaryWrite(bytes);
     }
     Response.Flush();
     Response.End();
 }

首先数组最大尺寸为int.MaxValue,然后符合规律程序是不会分这么大内存,很轻易搞挂服务器。(也正是能够下载的文本,极限值最多也就贰G不到。)【不推荐】

第二种:(Response.WriteFile)

public void FileDownload3()
{
    string fileName = "新建文件夹2.rar";//客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/新建文件夹2.rar");//要被下载的文件路径  
    FileInfo fileInfo = new FileInfo(filePath);
    Response.Clear();
    Response.ClearContent();
    Response.ClearHeaders();
    Response.AddHeader("Content-Disposition", "attachment;filename=\"" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8) + "\"");
    Response.AddHeader("Content-Length", fileInfo.Length.ToString());//文件大小
    Response.AddHeader("Content-Transfer-Encoding", "binary");
    Response.ContentType = "application/octet-stream";
    Response.WriteFile(fileInfo.FullName);//大小参数必须介于零和最大的 Int32 值之间(也就是最大2G,不过这个操作非常耗内存)
    //这里容易内存溢出
    Response.Flush();
    Response.End();
}

主题素材和率先类别似,也是无法下载当先二G的公文。然后下载大约②G文本时,机器也是高居被挂的边缘,万分恐怖。【不引入】

第三种:(Response.OutputStream.Write)

public void FileDownload4()
{
    string fileName = "大数据.rar";//客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径   

    if (System.IO.File.Exists(filePath))
    {
        const long ChunkSize = 102400; //100K 每次读取文件,只读取100K,这样可以缓解服务器的压力  
        byte[] buffer = new byte[ChunkSize];

        Response.Clear();
        using (FileStream fileStream = System.IO.File.OpenRead(filePath))
        {
            long fileSize = fileStream.Length; //文件大小  
            Response.ContentType = "application/octet-stream"; //二进制流
            Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
            Response.AddHeader("Content-Length", fileStream.Length.ToString());//文件总大小
            while (fileSize > 0 && Response.IsClientConnected)//判断客户端是否还连接了服务器
            {
                //实际读取的大小  
                int readSize = fileStream.Read(buffer, 0, Convert.ToInt32(ChunkSize));
                Response.OutputStream.Write(buffer, 0, readSize);
                Response.Flush();//如果客户端 暂停下载时,这里会阻塞。
                fileSize = fileSize - readSize;//文件剩余大小
            }
        }
        Response.Close();
    }
}

此间确定看出了是在循环读取输出,相比灵活。下载大文件时不曾压力。【推荐】

第四种:(Response.TransmitFile)
也就上上马举例说的这种,下载大文件也绝非压力。【推荐】

public void FileDownload5()
{          
    //前面可以做用户登录验证、用户权限验证等。

    string filename = "大数据.rar";   //客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径 

    Response.ContentType = "application/octet-stream";  //二进制流
    Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
    Response.TransmitFile(filePath); //将指定文件写入 HTTP 响应输出流
}

其余办法文件下载

在互连网查找C#文件下载一般都会搜到所谓的“二种方式”。其实那个代码并无法拿来直接行使,有坑的。
第一种:(Response.BinaryWrite)

 public void FileDownload2()
 {
     string fileName = "新建文件夹2.rar";//客户端保存的文件名  
     string filePath = Server.MapPath("/App_Data/新建文件夹2.rar");//要被下载的文件路径   

     Response.ContentType = "application/octet-stream";//二进制流
     //通知浏览器下载文件而不是打开  
     Response.AddHeader("Content-Disposition", "attachment;  filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));

     //以字符流的形式下载文件  
     using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
     {
         Response.AddHeader("Content-Length", fs.Length.ToString());
         //这里容易内存溢出
         //理论上数组最大长度 int.MaxValue 2147483647 
         //(实际分不到这么多,不同的程序能分到值也不同,本人机器,winfrom( 2147483591 相差56)、iis(也差不多2G)、iis Express(只有100多MB))
         byte[] bytes = new byte[(int)fs.Length];
         fs.Read(bytes, 0, bytes.Length);
         Response.BinaryWrite(bytes);
     }
     Response.Flush();
     Response.End();
 }

先是数组最大尺寸为int.马克斯Value,然后符合规律程序是不会分这么大内部存款和储蓄器,很轻便搞挂服务器。(也正是足以下载的文书,极限值最多也就二G不到。)【不推荐】

第二种:(Response.WriteFile)

public void FileDownload3()
{
    string fileName = "新建文件夹2.rar";//客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/新建文件夹2.rar");//要被下载的文件路径  
    FileInfo fileInfo = new FileInfo(filePath);
    Response.Clear();
    Response.ClearContent();
    Response.ClearHeaders();
    Response.AddHeader("Content-Disposition", "attachment;filename=\"" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8) + "\"");
    Response.AddHeader("Content-Length", fileInfo.Length.ToString());//文件大小
    Response.AddHeader("Content-Transfer-Encoding", "binary");
    Response.ContentType = "application/octet-stream";
    Response.WriteFile(fileInfo.FullName);//大小参数必须介于零和最大的 Int32 值之间(也就是最大2G,不过这个操作非常耗内存)
    //这里容易内存溢出
    Response.Flush();
    Response.End();
}

难题和第二种恍若,也是无法下载超过2G的文本。然后下载大概2G文书时,机器也是高居被挂的边缘,非常恐怖。【不推荐】

第三种:(Response.OutputStream.Write)

public void FileDownload4()
{
    string fileName = "大数据.rar";//客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径   

    if (System.IO.File.Exists(filePath))
    {
        const long ChunkSize = 102400; //100K 每次读取文件,只读取100K,这样可以缓解服务器的压力  
        byte[] buffer = new byte[ChunkSize];

        Response.Clear();
        using (FileStream fileStream = System.IO.File.OpenRead(filePath))
        {
            long fileSize = fileStream.Length; //文件大小  
            Response.ContentType = "application/octet-stream"; //二进制流
            Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8));
            Response.AddHeader("Content-Length", fileStream.Length.ToString());//文件总大小
            while (fileSize > 0 && Response.IsClientConnected)//判断客户端是否还连接了服务器
            {
                //实际读取的大小  
                int readSize = fileStream.Read(buffer, 0, Convert.ToInt32(ChunkSize));
                Response.OutputStream.Write(buffer, 0, readSize);
                Response.Flush();//如果客户端 暂停下载时,这里会阻塞。
                fileSize = fileSize - readSize;//文件剩余大小
            }
        }
        Response.Close();
    }
}

此间断定看出了是在循环读取输出,比较灵活。下载大文件时未尝压力。【推荐】

第四种:(Response.TransmitFile)
也就上起来比如说的这种,下载大文件也并未压力。【推荐】

public void FileDownload5()
{          
    //前面可以做用户登录验证、用户权限验证等。

    string filename = "大数据.rar";   //客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径 

    Response.ContentType = "application/octet-stream";  //二进制流
    Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
    Response.TransmitFile(filePath); //将指定文件写入 HTTP 响应输出流
}

文本下载-客户端

地点完毕了文件下载的服务端完结,接下去大家兑现公文下载的客户端完成。客户端的下载能够直接是浏览器提供的下载,也足以是迅雷可能大家友好写的下载程序。这里为了更加好的分析,大家来用winfrom程序自个儿写个下载客户端。

文本下载-客户端

地方达成了文件下载的服务端完毕,接下去大家兑现公文下载的客户端完结。客户端的下载能够直接是浏览器提供的下载,也足以是迅雷或然大家通力合作写的下载程序。这里为了越来越好的深入分析,大家来用winfrom程序本人写个下载客户端。

平昔下载

private async void button1_ClickAsync(object sender, EventArgs e)
{
    using (HttpClient http = new HttpClient())
    {
        var httpResponseMessage = await http.GetAsync("http://localhost:813/新建文件夹2.rar");//发送请求 (链接是a标签提供的)
        var contentLength = httpResponseMessage.Content.Headers.ContentLength;//读取文件大小
        using (var stream = await httpResponseMessage.Content.ReadAsStreamAsync())//读取文件流
        {
            var readLength = 1024000;//1000K  每次读取大小
            byte[] bytes = new byte[readLength];
            int writeLength;
            while ((writeLength = stream.Read(bytes, 0, readLength)) > 0)//分块读取文件流
            {
                using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write))//使用追加方式打开一个文件流
                {
                    fs.Write(bytes, 0, writeLength);//追加写入文件
                    contentLength -= writeLength;
                    if (contentLength == 0)//如果写入完成 给出提示
                        MessageBox.Show("下载完成");
                }
            }
        }
    } 
}

望着这么可以的代码,好像没难题。可实际往往适得其反。
韦德手机客户端 3

咱俩看到了三个卓绝“System.Net.Http.HttpRequestException:“不能够向缓冲区写入比所安顿最大缓冲区大小
21474836四柒更加的多的字节。”,什么鬼,又是214748364柒这么些数字。因为我们下载的文件大小当先了二G,不能缓冲下载。
唯独“缓冲下载”下又是怎么样鬼。小编也不驾驭。那我们搜求能够关掉这么些东东呢?答案是毫无疑问的。

var httpResponseMessage = await http.GetAsync("http://localhost:813/新建文件夹2.rar");//发送请求

改成上面就能够了

var httpResponseMessage = await http.GetAsync("http://localhost:813/新建文件夹2.rar",HttpCompletionOption.ResponseHeadersRead);//响应一可用且标题可读时即应完成的操作。 (尚未读取的内容。)

韦德手机客户端 4
咱俩来看枚举HttpCompletionOption的四个值。三个是响应读取内容,2个是响应读取标题(也正是Headers里的内容)。

直白下载

private async void button1_ClickAsync(object sender, EventArgs e)
{
    using (HttpClient http = new HttpClient())
    {
        var httpResponseMessage = await http.GetAsync("http://localhost:813/新建文件夹2.rar");//发送请求 (链接是a标签提供的)
        var contentLength = httpResponseMessage.Content.Headers.ContentLength;//读取文件大小
        using (var stream = await httpResponseMessage.Content.ReadAsStreamAsync())//读取文件流
        {
            var readLength = 1024000;//1000K  每次读取大小
            byte[] bytes = new byte[readLength];
            int writeLength;
            while ((writeLength = stream.Read(bytes, 0, readLength)) > 0)//分块读取文件流
            {
                using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write))//使用追加方式打开一个文件流
                {
                    fs.Write(bytes, 0, writeLength);//追加写入文件
                    contentLength -= writeLength;
                    if (contentLength == 0)//如果写入完成 给出提示
                        MessageBox.Show("下载完成");
                }
            }
        }
    } 
}

看着这么杰出的代码,好像没问题。可实际往往适得其反。
韦德手机客户端 3

小编们见到了1个极度“System.Net.Http.HttpRequestException:“不能够向缓冲区写入比所安顿最大缓冲区大小
2147483647越来越多的字节。”,什么鬼,又是21474836四7以此数字。因为大家下载的文件大小抢先了二G,不能够缓冲下载。
但是“缓冲下载”下又是怎么着鬼。笔者也不知晓。那大家探求能够关掉这么些东东吗?答案是任其自流的。

var httpResponseMessage = await http.GetAsync("http://localhost:813/新建文件夹2.rar");//发送请求

改成上面就足以了

var httpResponseMessage = await http.GetAsync("http://localhost:813/新建文件夹2.rar",HttpCompletionOption.ResponseHeadersRead);//响应一可用且标题可读时即应完成的操作。 (尚未读取的内容。)

韦德手机客户端 4
小编们看来枚举HttpCompletionOption的七个值。八个是响应读取内容,一个是响应读取题目(也正是Headers里的内容)。

异步下载

大家发掘在下载大文件的时候会促成界面假死。那是UI单线程程序的缺陷。当然,这么差的用户体验是大家不能够忍受的。上边大家为下载开一个线程,制止形成UI线程的短路。

/// <summary>
/// 异步下载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button2_ClickAsync(object sender, EventArgs e)
{
    //开启一个异步线程
    await Task.Run(async () =>
    {
        //异步操作UI元素
        label1.Invoke((Action)(() =>
                {
                    label1.Text = "准备下载...";
                }));

        long downloadSize = 0;//已经下载大小
        long downloadSpeed = 0;//下载速度
        using (HttpClient http = new HttpClient())
        {
            var httpResponseMessage = await http.GetAsync("http://localhost:813/新建文件夹2.rar", HttpCompletionOption.ResponseHeadersRead);//发送请求
            var contentLength = httpResponseMessage.Content.Headers.ContentLength;   //文件大小                
            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)
                {
                    //使用追加方式打开一个文件流
                    using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write))
                    {
                        fs.Write(bytes, 0, writeLength);
                    }
                    downloadSize += writeLength;
                    downloadSpeed += 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)(downloadSize * 100 / contentLength), 1);
                    }));
                }

                label1.Invoke((Action)(() =>
                {
                    label1.Text = "下载完成";
                }));
            }
        }
    });
}

效果图:
韦德手机客户端 7

异步下载

咱俩开采在下载大文件的时候会导致分界面假死。那是UI单线程程序的败笔。当然,这么差的用户体验是大家无法隐忍的。下边大家为下载开一个线程,幸免产生UI线程的围堵。

/// <summary>
/// 异步下载
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void button2_ClickAsync(object sender, EventArgs e)
{
    //开启一个异步线程
    await Task.Run(async () =>
    {
        //异步操作UI元素
        label1.Invoke((Action)(() =>
                {
                    label1.Text = "准备下载...";
                }));

        long downloadSize = 0;//已经下载大小
        long downloadSpeed = 0;//下载速度
        using (HttpClient http = new HttpClient())
        {
            var httpResponseMessage = await http.GetAsync("http://localhost:813/新建文件夹2.rar", HttpCompletionOption.ResponseHeadersRead);//发送请求
            var contentLength = httpResponseMessage.Content.Headers.ContentLength;   //文件大小                
            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)
                {
                    //使用追加方式打开一个文件流
                    using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write))
                    {
                        fs.Write(bytes, 0, writeLength);
                    }
                    downloadSize += writeLength;
                    downloadSpeed += 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)(downloadSize * 100 / contentLength), 1);
                    }));
                }

                label1.Invoke((Action)(() =>
                {
                    label1.Text = "下载完成";
                }));
            }
        }
    });
}

效果图:
韦德手机客户端 7

断点续传

上面的主意大家开采,假如下载到2个半断网了下一次会重头开首下载。那和大家明天的大旨明显不合嘛。下边大家初步正式进入正题文件下载之断点续传。把前边大家谈起的头属性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 = "http://localhost: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 = "暂停下载";
    }
}

效果图:
韦德手机客户端 9

到今日达成,你感到大家的断点续传就成功了啊?
错,你有未有觉察大家运用的下载链接是a标签的。也等于大家精诚团结写服务端提供的下载链接是否也能够帮衬断点续传呢?下边作者换个下载链接试试便知。

断点续传

地方的章程大家发掘,假设下载到2个半断网了后一次会重头初始下载。那和大家后天的大旨明显不合嘛。上面我们开首正式进入正题文件下载之断点续传。把前边大家聊到的头属性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 = "http://localhost: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 = "暂停下载";
    }
}

效果图:
韦德手机客户端 9

到近些日子结束,你以为大家的断点续传就成功了啊?
错,你有未有觉察大家采取的下载链接是a标签的。也正是大家友好写服务端提供的下载链接是否也足以支撑断点续传呢?上面作者换个下载链接试试便知。

断点续传(服务端的支撑)

测试结果如下:
韦德手机客户端 11

发觉并不帮助断点续传。为啥a标签链接能够直接帮衬,大家写的下载却不支持啊。
a标签的链接指向的直白是iis上的公文(iis私下认可补助),而我们写的却从没做响应报文表头Range的管理。(没想象中的那么智能嘛
>_<)

前方大家说过,断线续传是HTTP的叁个体协会谈商讨。大家遵循它,它就存在,我们不服从它也就不设有。
这上面大家修改前边的文本下载代码(服务端):

public void FileDownload5()
{          
    //前面可以做用户登录验证、用户权限验证等。

    string filename = "大数据.rar";   //客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径 

    var range = Request.Headers["Range"];
    if (!string.IsNullOrWhiteSpace(range))//如果遵守协议,支持断点续传
    {
        var fileLength = new FileInfo(filePath).Length;//文件的总大小
        long begin;//文件的开始位置
        long end;//文件的结束位置
        long.TryParse(range.Split('=')[1].Split('-')[0], out begin);
        long.TryParse(range.Split('-')[1], out end);
        end = end - begin > 0 ? end : (fileLength - 1);// 如果没有结束位置,那我们读剩下的全部

        //表头 表明  下载文件的开始、结束位置 和文件总大小
        Response.AddHeader("Content-Range", "bytes " + begin + "-" + end + "/" + fileLength);
        Response.ContentType = "application/octet-stream";
        Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
        Response.TransmitFile(filePath, begin, (end - begin));//发送 文件开始位置读取的大小
    }
    else
    {
        Response.ContentType = "application/octet-stream";
        Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
        Response.TransmitFile(filePath);
    }
}

下一场再测试断点续传,完美帮助。

断点续传(服务端的支撑)

测试结果如下:
韦德手机客户端 11

意识并不协理断点续传。为啥a标签链接能够直接协助,我们写的下载却不扶助呢。
a标签的链接指向的一向是iis上的文本(iis私下认可扶助),而我们写的却尚未做响应报文表头Range的管理。(没想象中的那么智能嘛
>_<)

眼前大家说过,断线续传是HTTP的二个体协会议。大家遵循它,它就存在,大家不信守它也就不存在。
那下边我们修改前面的文书下载代码(服务端):

public void FileDownload5()
{          
    //前面可以做用户登录验证、用户权限验证等。

    string filename = "大数据.rar";   //客户端保存的文件名  
    string filePath = Server.MapPath("/App_Data/大数据.rar");//要被下载的文件路径 

    var range = Request.Headers["Range"];
    if (!string.IsNullOrWhiteSpace(range))//如果遵守协议,支持断点续传
    {
        var fileLength = new FileInfo(filePath).Length;//文件的总大小
        long begin;//文件的开始位置
        long end;//文件的结束位置
        long.TryParse(range.Split('=')[1].Split('-')[0], out begin);
        long.TryParse(range.Split('-')[1], out end);
        end = end - begin > 0 ? end : (fileLength - 1);// 如果没有结束位置,那我们读剩下的全部

        //表头 表明  下载文件的开始、结束位置 和文件总大小
        Response.AddHeader("Content-Range", "bytes " + begin + "-" + end + "/" + fileLength);
        Response.ContentType = "application/octet-stream";
        Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
        Response.TransmitFile(filePath, begin, (end - begin));//发送 文件开始位置读取的大小
    }
    else
    {
        Response.ContentType = "application/octet-stream";
        Response.AddHeader("Content-Disposition", "attachment;filename=" + filename);
        Response.TransmitFile(filePath);
    }
}

然后再测试断点续传,完美帮忙。

三十二线程同一时间下载(分片下载)

文件的断点续传已经剖判完了。不过中间稍微细节的东西你能够依靠实际要求去完善。如:文件命名、断点续传的文书是不是发生了改动、下载落成后证实文件和服务器上的是还是不是1律。
还也许有大家能够依赖表头属性Range来完成十2线程下载,但是这里就不贴代码了,贴个功效图吧。和上1篇韦德手机客户端,文本上传里的多线程上传同理。您也得以依照提供的demo代码下载查看,内有完整兑现。
韦德手机客户端 13

 

仿照效法资料

demo

二十八线程同临时间下载(分片下载)

文件的断点续传已经分析完了。可是中间稍微细节的东西你可以遵照实际须求去完善。如:文件命名、断点续传的公文是否发生了退换、下载达成后证实文件和服务器上的是不是同样。
还应该有我们得以依赖表头属性Range来完结十六线程下载,不过这里就不贴代码了,贴个职能图吧。和上1篇文本上传里的多线程上传同理。您也能够依据提供的demo代码下载查看,内有完全兑现。
韦德手机客户端 13

 

参谋资料

demo

 

相关文章