C〓〓〓〓はどのようにファイルをダウンロードして断点更新を実現しますか?


前言
古いしきたりは、やはり最も簡単で乱暴なものから始まる。どれぐらい簡単ですか?どれぐらい乱暴したら乱暴になりますか?コードを書かなくてもいいです。信じますか?一つのファイルを直接IISサーバーに投げたらダウンロードできます。TMを返してもいいですか?断点継続(IISサーバーのデフォルトサポート)。
コードを貼る前にまず何が断点継続ですか?どのようにしてブレークポイントを更新しますか?
断時更新は途中でネットを切ったり一時停止したりしました。その後ダウンロードできます。最初からダウンロードしなくてもいいです。
不思議ですか?実は簡単です。私達も考えてみます。
まず、クライアントはサービス端末に要求を送信する(ファイルをダウンロードする)。その後、サービス端末は要求に応答し、ファイルのサイズ全体、ファイルフローの開始位置、終了位置、コンテンツサイズなどを含む。具体的にはどうやって実現されますか?
HTTP/1.1はヘッド属性Rangeがあります。例えば、あなたが要求を送る時、Range:0-199を持参すると、0から199までの間のデータを要求するのと同じです。その後、サーバー応答要求Content-Range:bytes 0-199/250は、あなたが0から199までの間のデータを取得したと表しています。合計サイズは250です。つまり、まだデータがダウンロードされていないということです。
図を描きましょう。

簡単ですか?このような不思議なものはつまり「約束」です。つまりHTTPプロトコルです。
しかし、契約というものは守るものがあります。守らないと存在しません。民国時代のお金のようにみんながそれを信じて、それが役に立ちます。ほとんどの人がそれを信じないなら、卵がないです。
このブレークポイントは継続しても同じです。あなたが服務して守るなら支持します。守らないなら、断続的に継続することを支持しません。ですから、私たちはダウンロードツールを書く時に、応答文にConteet-Rangeがあるかどうかを判断して、断点更新をサポートする必要があります。
余計なことを言うと,下にそでをまくり上げて乾かす.
ファイルのダウンロード
aタグを使ってファイルのダウンロードを提供します。
aタグを使ってファイルをダウンロードします。つまりコードを書かないとダウンロードできます。直接にファイルをiisサーバーに投げて、リンクをaラベルに貼り付けて完了します。

<a href="/     2.rar" rel="external nofollow" >  </a>
簡単、乱暴は言うまでもない。本当にこんなに良いなら、他のダウンロードロジックを書くのに苦労しません。ここに致命的な欠点がある。このような方式で提供されるダウンロードは安全ではありません。誰でもダウンロードできます。制御する権限がないので、ファイルスキャンされるかもしれません。
Response.Transmittleを使ってファイルのダウンロードを提供します。
上で直接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〓〓〓ファイルを検索してダウンロードして普通はいずれもいわゆる“4種類の方式”を探し当てます。実はそのコードは直接使用できません。穴があります。
第一種類:(Resonse.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です。そして、通常のプログラムはこのようなメモリがないので、サーバーに接続しやすいです。つまりダウンロードできるファイルの限界値はせいぜい2 G以下です。おすすめしません
第二種類:(Resonse.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();
}
問題は最初と同じです。2 G以上のファイルをダウンロードすることもできません。2 Gぐらいのファイルをダウンロードしても、機械がかけられた縁にあるので、かなり怖いです。おすすめしません
第三種類:(Resoponse.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.TransmittFile)
上で例を挙げてみます。大きなファイルをダウンロードしても大丈夫です。おすすめ)

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      
}
ファイルのダウンロード-クライアント
上記はファイルダウンロードのサービスを実現しました。これからファイルダウンロードのクライアントを実現します。クライアントのダウンロードは直接ブラウザから提供されてもいいし、雷や自分で書いたダウンロードプログラムでもいいです。ここでは、より良い分析のために、Windowsプログラムを使って自分でダウンロードクライアントを書き込みます。
直接ダウンロード

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;
            using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write))//             
            {
                while ((writeLength = stream.Read(bytes, 0, readLength)) > 0)//       
                {
                    fs.Write(bytes, 0, writeLength);//      
                    contentLength -= writeLength;
                    if (contentLength == 0)//           
                        MessageBox.Show("    ");
                }
            }
        }
    } 
}
こんなに綺麗なコードを見ていますが、大丈夫みたいです。しかし現実は往々にして思い通りにならない。

私たちは異常を見ました。「System.Net.Http.HttpRequest Exception:設定された最大バッファサイズよりも多くのバイトをバッファに書き込むことができません。」何の鬼か、また2147483647という数字です。ダウンロードしたファイルのサイズが2 Gを超えていますので、ダウンロードをバッファリングできません。
しかし、「バッファダウンロード」の下にはどんな鬼がいますか?私も分かりません。これを消してもいいですか?答えは肯定的です。

var httpResponseMessage = await http.GetAsync("http://localhost:813/     2.rar");//    
下に変えたらいいです。

var httpResponseMessage = await http.GetAsync("http://localhost:813/     2.rar",HttpCompletionOption.ResponseHeadersRead);//                  。 (       。)

私たちはエニュメレーションHttpComplettion Optionの二つの値を見ました。一つは内容の読み取りに応答し、一つはタイトルの読み取りに応答することである。
【注意】using (FileStream fs = new FileStreamwhile ((writeLength =の外に置くべきです。そうでないと、書き込みファイルが占有される異常が発生する可能性があります。
非同期ダウンロード
私たちは大きなファイルをダウンロードする時にインターフェースの仮死を引き起こすことを発見しました。これは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;//     
                //             
                using (FileStream fs = new FileStream(Application.StartupPath + "/temp.rar", FileMode.Append, FileAccess.Write))
                {
                    while ((writeLength = stream.Read(bytes, 0, readLength)) > 0)
                    {                 
                         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 = "    ";
                }));
            }
        }
    });
}
効果図:

断点更新
上の方法は私達は発見しました。もし半分のネットをダウンロードしたら、今度は再びダウンロードを開始します。今日のテーマとは明らかに違っていますね。次に私たちは本題 に入ります。前に話した頭の属性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 = "    ";
    }
}
効果図:

今までのところ、私たちは断続的に延長すればいいと思いますか?
間違っています。私たちが使っているダウンロードリンクはaタグです。つまり、自分でサーバーから提供されたダウンロードリンクを書いても、断点継続をサポートできますか?次はダウンロードリンクに変えてみます。
断時更新(サービスサポート)
テストの結果は以下の通りです。

ブレークポイントの継続はサポートされていないことが分かりました。なぜ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によってマルチスレッドのダウンロードができますが、ここではコードを貼りません。効果図を貼りましょう。前のファイルアップロードのマルチスレッドと同じです。あなたも提供したデモコードによってダウンロードして見ることができます。

以上はC〓〓〓〓を実現してどのようにファイルをダウンロードして断点更新の詳しい内容をダウンロードして、もっと多いC〓〓ファイルに関して断点更新の資料をダウンロードします。私達のその他の関連している文章に注目してください。