補足:ここではHTTPについて多少触れていますが、私自身それほど詳しくなく、さらに.NETから内容が離れてしまうため、ごく簡単にしか説明しません。HTTPに関して詳しくはRFC2616等をご覧ください。
リジュームダウンロードとは、以前途中までダウンロードしたファイルがあるとき、その続きからダウンロードを再開することです。
「WebRequest、WebResponseクラスを使ってファイルをダウンロードし保存する」に少し手を加えるだけでリジュームダウンロードができるようになります。HttpWebRequestクラスのAddRangeメソッドによりダウンロードするデータの開始位置を取得し、すでに保存してあるファイルの末端に追加して書き込みます。AddRangeメソッドはつまり、Rangeヘッダフィールドを指定するものです。
基本的にはこれだけですが、いくつかの問題が残ります。
まず、本当に指定した範囲のデータをサーバーが送ってきたかをどのように判断するかという問題です。これにはHTTP応答ステータスコードを調べ、206(Partial Content)であれば成功したと判断する方法が考えられます。また、Content-Rangeヘッダフィールドを調べれば、実際に送られてきたデータの範囲を取得できます。
また、途中までダウンロードしたデータが今サーバーにあるデータと同じものであるか確かめる必要もあるでしょう。これには、If-Rangeや、If-Match、If-Unmodified-Sinceヘッダなどを使うとよいでしょう。
以上の知識を基に、MSDNのロゴを保存するサンプルを以下に示します。ここでは保存先のファイルがある時はダウンロードが途中であると判断し、リジュームダウンロードするようにしています。
'ダウンロードするファイル Dim url As String = "http://www.msj.co.jp/msdn/images/msdn_logo.jpg" '保存先のファイル名 Dim fileName As String = "c:\logo.jpg" 'If-Rangeヘッダに渡すエンティティタグを指定するときは指定する Dim entityTag As String = "" 'WebRequestの作成 Dim webreq As System.Net.HttpWebRequest = _ CType(System.Net.WebRequest.Create(url), _ System.Net.HttpWebRequest) 'ファイルがあればダウンロードが途中であると判断し、 'ファイルサイズを取得する Dim firstPos As Long If System.IO.File.Exists(fileName) Then firstPos = (New System.IO.FileInfo(fileName)).Length Else firstPos = 0 End If If firstPos > 0 Then 'バイトレンジを指定する webreq.AddRange(CInt(firstPos)) 'If-Rangeヘッダを追加 If entityTag <> "" Then webreq.Headers.Add("If-Range", entityTag) End If End If 'そのほかのヘッダを指定する webreq.KeepAlive = False webreq.Headers.Add("Pragma", "no-cache") webreq.Headers.Add("Cache-Control", "no-cache") Dim webres As System.Net.HttpWebResponse = Nothing Try 'サーバーからの応答を受信するためのWebResponseを取得 webres = CType(webreq.GetResponse(), System.Net.HttpWebResponse) Catch e As System.Net.WebException 'HTTPプロトコルエラーを捕捉し、内容を表示する If e.Status = System.Net.WebExceptionStatus.ProtocolError Then Dim errres As System.Net.HttpWebResponse = _ CType(e.Response, System.Net.HttpWebResponse) Console.WriteLine(errres.StatusCode) Console.WriteLine(errres.StatusDescription) Else Console.WriteLine(e.Message) End If Return End Try 'エンティティタグの表示 Console.WriteLine(("ETag:" + webres.GetResponseHeader("ETag"))) '応答データを受信するためのStreamを取得 Dim strm As System.IO.Stream = webres.GetResponseStream() 'ファイルに書き込むためのFileStreamを作成 Dim fs As New System.IO.FileStream(fileName, _ System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write) 'ファイルに書き込む位置を決定する '206Partial Contentステータスコードが返された時はContent-Rangeヘッダを調べる 'それ以外のときは、先頭から書き込む Dim seekPos As Long = 0 If webres.StatusCode = System.Net.HttpStatusCode.PartialContent Then Dim contentRange As String = _ webres.GetResponseHeader("Content-Range") Dim m As System.Text.RegularExpressions.Match = _ System.Text.RegularExpressions.Regex.Match(contentRange, _ "bytes\s+(?:(?<first>\d*)-(?<last>\d*)|\*)/(?:(?<len>\d+)|\*)") If m.Groups("first").Value = "" Then seekPos = 0 Else seekPos = Integer.Parse(m.Groups("first").Value) End If End If '書き込み位置を変更する fs.SetLength(seekPos) fs.Position = seekPos '応答データをファイルに書き込む Dim readData(1023) As Byte Dim readSize As Integer = 0 While True readSize = strm.Read(readData, 0, readData.Length) If readSize = 0 Then Exit While End If fs.Write(readData, 0, readSize) End While '閉じる fs.Close() strm.Close()
//ダウンロードするファイル string url = "http://www.msj.co.jp/msdn/images/msdn_logo.jpg"; //保存先のファイル名 string fileName = "c:\\logo.jpg"; //If-Rangeヘッダに渡すエンティティタグを指定するときは指定する string entityTag = ""; //WebRequestの作成 System.Net.HttpWebRequest webreq = (System.Net.HttpWebRequest) System.Net.WebRequest.Create(url); //ファイルがあればダウンロードが途中であると判断し、 //ファイルサイズを取得する long firstPos; if (System.IO.File.Exists(fileName)) firstPos = (new System.IO.FileInfo(fileName)).Length; else firstPos = 0; if (firstPos > 0) { //バイトレンジを指定する webreq.AddRange((int) firstPos); //If-Rangeヘッダを追加 if (entityTag != "") webreq.Headers.Add("If-Range", entityTag); } //そのほかのヘッダを指定する webreq.KeepAlive = false; webreq.Headers.Add("Pragma", "no-cache"); webreq.Headers.Add("Cache-Control", "no-cache"); System.Net.HttpWebResponse webres = null; try { //サーバーからの応答を受信するためのWebResponseを取得 webres = (System.Net.HttpWebResponse) webreq.GetResponse(); } catch (System.Net.WebException e) { //HTTPプロトコルエラーを捕捉し、内容を表示する if (e.Status == System.Net.WebExceptionStatus.ProtocolError) { System.Net.HttpWebResponse errres = (System.Net.HttpWebResponse) e.Response; Console.WriteLine(errres.StatusCode); Console.WriteLine(errres.StatusDescription); } else Console.WriteLine(e.Message); return; } //エンティティタグの表示 Console.WriteLine("ETag:" + webres.GetResponseHeader("ETag")); //応答データを受信するためのStreamを取得 System.IO.Stream strm = webres.GetResponseStream(); //ファイルに書き込むためのFileStreamを作成 System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write); //ファイルに書き込む位置を決定する //206Partial Contentステータスコードが返された時はContent-Rangeヘッダを調べる //それ以外のときは、先頭から書き込む long seekPos = 0; if (webres.StatusCode == System.Net.HttpStatusCode.PartialContent) { string contentRange = webres.GetResponseHeader("Content-Range"); System.Text.RegularExpressions.Match m = System.Text.RegularExpressions.Regex.Match( contentRange, @"bytes\s+(?:(?<first>\d*)-(?<last>\d*)|\*)/(?:(?<len>\d+)|\*)"); if (m.Groups["first"].Value == "") seekPos = 0; else seekPos = int.Parse(m.Groups["first"].Value); } //書き込み位置を変更する fs.SetLength(seekPos); fs.Position = seekPos; //応答データをファイルに書き込む byte[] readData = new byte[1024]; int readSize = 0; for (;;) { readSize = strm.Read(readData, 0, readData.Length); if (readSize == 0) break; fs.Write(readData, 0, readSize); } //閉じる fs.Close(); strm.Close();