亚洲必赢手机【转】文件下充斥之断点续传(客户端和服务端的兑现)【转】文件下充斥之断点续传(客户端和劳动端的落实)

by admin on 2018年10月5日

【转】文件下充斥之断点续传(客户端与劳动端的贯彻)

【转】文件下充斥的断点续传(客户端与服务端的兑现)

前方说了文件之达标污染,今天来聊聊文件的下载。

前说了文本的达到传,今天来聊聊文件的下载。

规矩,还是于不过简单易行粗暴的开始。那么多简单算简单?多多少暴算粗暴?我报你可免写一句代码,你奉吗?直接将一个文书于IIS服务器上等同扔,就支持下载。还TM么可以断点续传(IIS服务端默认支持)。

老,还是打极度简单易行粗暴的启幕。那么基本上简单算简单?多有点暴算粗暴?我报告您得无写一词代码,你奉为?直接把一个文书为IIS服务器上同扔,就支持下载。还TM么可以断点续传(IIS服务端默认支持)。

于粘贴代码之前先来打听下啊是断点续传(此间说的凡下载断点续传)?亚洲必赢手机怎么落实之断点续传?
断点续传就是生充斥了大体上断网或者暂停了,然后可以就下载。不用从头开始下载。

在贴代码之前先行来打探下啊是断点续传(这里说之是下载断点续传)?怎么落实的断点续传?
断点续传就是生充斥了一半断网或者暂停了,然后可以随着下载。不用从头开始下载。

雅神奇呢,其实简单得那个,我们想也是足以想到的。
第一客户端向服务端发送一个求(下载文件)。然后服务端响应请求,信息包含文件总大小、文件流开始同了位置、内容大小等。那现实是怎落实的也罢?
HTTP/1.1生身材属性Range。比如你发送请求的早晚带达Range:0-199,等于你是告求0到199里面的数量。然后服务器响应请求Content-Range: bytes 0-199/250
,表示若收获了0到199中的多少,总大小是250。(也就算是喻您还有数量没有下载了)。

杀神奇吗,其实简单得不行,我们思想也是可想到的。
第一客户端向服务端发送一个请(下载文件)。然后服务端响应请求,信息包含文件到底大小、文件流开始同终止位置、内容大小相当于。那现实是怎么落实之也?
HTTP/1.1来身材属性Range。比如您发送请求的时候带齐Range:0-199,等于你是请求求0到199里头的数量。然后服务器响应请求Content-Range: bytes 0-199/250
,表示若拿走了0到199之内的数额,总大小是250。(也就是是报告您还有数目没有下载了)。

咱俩来作画个图吧。
亚洲必赢手机 1

我们来写个图吧。
亚洲必赢手机 2

大凡不是那个粗略?这么神奇的事物吗尽管是只“约定”而已,也不怕是所谓的HTTP协议。
而,协议这东西而恪守其便有,不守其就是未在。就比如民国时代的钱大家还信其,它便起因此。如果大部分人数不信它,也即从未有过卵用了。
此断点续传也是这么。你服务端遵守就支持,不遵循也就是无支持断点续传。所以我们形容下载工具的时候需要判定响应报文里来没有来Content-Range,来规定是不是支持断点续传。
废话够多了,下面撸起袖子开干。

是未是充分简短?这么神奇的东西也就是只“约定”而已,也就算是所谓的HTTP协议。
而是,协议这东西而恪守其就存在,不听命其便非有。就比如民国时期的钱大家都信其,它就来因此。如果大部分人不信它,也不怕没有卵用了。
此断点续传也是这样。你服务端遵守就支持,不信守也就算无支持断点续传。所以我们刻画下载工具的上需要看清响应报文里发生没有起Content-Range,来确定是否支持断点续传。
废话够多了,下面撸起袖子开干。

文件下充斥-服务端

文本下充斥-服务端

应用a标签提供文件下充斥

运用a标签来下充斥文件,也就是咱们眼前说的免写代码就可兑现下载。直接将文件为iis服务器上一致扔,然后拿链接贴到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,然后正常程序是免会见分这么可怜内存,很容易折腾挂服务器。(否就是是好下载的文本,极限值最多也不怕2G不至。)【不推荐】

第二种:(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 响应输出流
}

旁艺术文件下充斥

当网上寻找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,然后正常程序是匪会见分这么好内存,很爱为挂服务器。(为即是好下载的文书,极限值最多也便2G不至。)【不推荐】

第二种:(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:“不可知为缓冲区勾勒副于所安排最深缓冲区大小
2147483647
更多之字节。”,什么不良,又是2147483647以此数字。因为我们下载的文件大小超过了2G,无法缓冲下充斥。
只是“缓冲下充斥”下同时是什么不良。我呢非知情。那我们试试可以合这个东东吧?答案是必之。

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里之情节)。

直下载

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("下载完成");
                }
            }
        }
    } 
}

扣押在如此精美的代码,好像没问题。可实际为往事与愿违。
亚洲必赢手机 5

咱俩看了一个老大“System.Net.Http.HttpRequestException:“不可知朝缓冲区写副于所安排最要命缓冲区大小
2147483647
更多的字节。”,什么不良,又是2147483647者数字。因为咱们下载的文件大小超过了2G,无法缓冲下充斥。
然而“缓冲下充斥”下而是啊不良。我吧无明白。那我们尝试可以合这个东东啊?答案是早晚之。

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

反成为下面就是可了

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

亚洲必赢手机 6
咱看枚举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 = "下载完成";
                }));
            }
        }
    });
}

效果图:
亚洲必赢手机 8

断点续传

地方的章程我们发现,如果下充斥至一个半断网了下次会重头开始下载。这和我们今天的主题明确不符嘛。下面我们初步正儿八经入正题文件下载之断点续传。把前面我们说到的头属性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标签的。也便是我们友好写服务端提供的下载链接是匪是为足以支撑断点续传呢?下面我改换个下充斥链接试试就明白。

断点续传

点的方法我们发现,如果下充斥至一个半断网了下次会见重头开始下载。这同咱们今天底主题鲜明不符嘛。下面我们开专业上正题文件下载之断点续传。把前面我们说到的头属性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 = "暂停下载";
    }
}

效果图:
亚洲必赢手机 10

及现寿终正寝,你觉得我们的断点续传就到位了也?
摩擦,你产生没发出觉察我们应用的下载链接是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);
    }
}

接下来再次测试断点续传,完美支持。

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

测试结果如下:
亚洲必赢手机 12

察觉并无支持断点续传。为什么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);
    }
}

接下来还测试断点续传,完美支持。

多线程同时下载(分片下载)

文件的断点续传已经分析了了。不过中间稍微细节之物而可因实际要求去完善。如:文件命名、断点续传的文书是否出了反、下载就后证文件以及服务器上之是不是相同。
再有咱们得以因表头属性Range来实现多线程下载,不过此就是未贴代码了,贴单效益图吧。和达成同样篇文本及污染里之多线程上传同理。您为得以依据提供的demo代码下载查看,内发出完兑现。
亚洲必赢手机 13

 

参考资料

  • http://www.cnblogs.com/yank/p/HTTP-Range.html
  • http://www.cnblogs.com/sparkdev/p/6141539.html

demo

  • https://github.com/zhaopeiym/BlogDemoCode/tree/master/上传下载

 

大多线程同时下载(分片下载)

文本之断点续传已经分析了了。不过中间小细节之事物而可以根据实际需求去圆。如:文件命名、断点续传的文书是否发了反、下载完成后证实文件以及服务器上之是否一致。
再有咱们得以根据表头属性Range来实现多线程下载,不过这里虽未糊代码了,贴个效益图吧。和齐一致首文本及污染里的多线程上传同理。您吗得根据提供的demo代码下载查看,内出整机兑现。
亚洲必赢手机 14

 

参考资料

  • http://www.cnblogs.com/yank/p/HTTP-Range.html
  • http://www.cnblogs.com/sparkdev/p/6141539.html

demo

  • https://github.com/zhaopeiym/BlogDemoCode/tree/master/上传下载

 

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图