4 using System.Collections;
\r
5 using System.Threading;
\r
6 using NaGet.SubCommands;
\r
12 /// ダウンロードイベントオブジェクト
\r
14 public class DownloadEventArgs : NaGetEventArgs
\r
19 public DownloadEventType Type;
\r
24 public long DownloadSize;
\r
29 public long MaxSize;
\r
34 /// <param name="type">イベントの種類</param>
\r
35 /// <param name="msg">イベントメッセージ</param>
\r
36 /// <param name="pos">ダウンロード済みのバイト数</param>
\r
37 /// <param name="max">ダウンロードする総バイト数</param>
\r
38 public DownloadEventArgs(DownloadEventType type, string msg, long pos, long max)
\r
45 TaskProgressPercent = (max > 0)? (100.0f * pos / max) : -1;
\r
50 /// DownloadEventArgsでのイベントの種類
\r
52 public enum DownloadEventType {
\r
64 public class Downloader : NaGetTask
\r
69 public static IWebProxy DefaultProxy = WebRequest.GetSystemWebProxy();
\r
74 public IWebProxy Proxy;
\r
79 public event EventHandler<DownloadEventArgs> DownloadEventRaised;
\r
84 protected string url;
\r
89 protected string filepath;
\r
94 protected WebRequest request;
\r
97 /// レスポンスオブジェクト。応答がくるまではnullである。
\r
99 protected WebResponse response;
\r
102 /// ダウンロード要求時のキャッシュレベル。デフォルトではキャッシュ無視
\r
104 public System.Net.Cache.RequestCacheLevel CacheLevel = System.Net.Cache.RequestCacheLevel.NoCacheNoStore;
\r
106 private bool cancelCalled = false;
\r
108 private bool done = false;
\r
111 /// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名
\r
113 private string downloadedFileName = null;
\r
116 /// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名
\r
118 public string DownloadedFileName {
\r
119 get { return downloadedFileName; }
\r
122 /* ダウンロードの処理時間計測器 */
\r
123 private System.Diagnostics.Stopwatch stopwatch;
\r
128 /// <param name="url">ダウンロードするリソースのURL</param>
\r
129 /// <param name="filepath">保存先ファイルパス</param>
\r
130 public void Download(string url, string filepath)
\r
133 this.filepath = filepath;
\r
137 } catch (NotSupportedException e) {
\r
138 throw new NotSupportedException("Not supported download location for downloader.", e);
\r
142 public override bool Done {
\r
143 get { return done; }
\r
146 public override bool Running {
\r
147 get { return request != null && (!done); }
\r
150 public override void Run()
\r
152 RaiseDownloadEvent(DownloadEventType.INITED, 0, -1);
\r
155 request = WebRequest.Create(url);
\r
156 request.Proxy = (Proxy == null)? DefaultProxy : Proxy;
\r
157 request.CachePolicy = new System.Net.Cache.RequestCachePolicy(CacheLevel);
\r
159 if (cancelCalled) {
\r
160 throw new NaGetTaskCanceledException(string.Empty);
\r
164 response = request.GetResponse();
\r
165 } catch (WebException e) {
\r
166 if (cancelCalled) { // キャンセル時
\r
167 throw new NaGetTaskCanceledException(string.Empty, e);
\r
169 throw new WebException(e.Message, e);
\r
173 if (cancelCalled) {
\r
174 throw new NaGetTaskCanceledException(string.Empty);
\r
178 downloadedFileName = getFileNameFromWebResponse(response);
\r
179 } catch (Exception) {
\r
182 if (File.Exists(filepath)) { // ファイルが存在するとき削除
\r
183 File.Delete(filepath);
\r
186 RaiseDownloadEvent(DownloadEventType.CONNECTED, 0, -1);
\r
188 using (Stream stream = response.GetResponseStream() )
\r
189 using (FileStream fs = new FileStream(filepath,
\r
191 FileAccess.Write) ) {
\r
193 File.SetAttributes(filepath, FileAttributes.Hidden);
\r
194 long contentLength = response.ContentLength;
\r
196 RaiseDownloadEvent(DownloadEventType.STARTED, 0, contentLength);
\r
198 stopwatch = new System.Diagnostics.Stopwatch();
\r
201 Timer timer = new Timer(new TimerCallback(
\r
202 delegate(object obj) {
\r
204 RaiseDownloadEvent(DownloadEventType.DOWNLOADING, fs.Position, contentLength);
\r
205 } catch (ObjectDisposedException) {
\r
207 }), null, 0, 1000);
\r
210 byte[] data = new byte[4096];
\r
212 while ((size = stream.Read(data,0,data.Length)) > 0) {
\r
213 fs.Write(data, 0, size);
\r
215 if (cancelCalled) {
\r
216 throw new NaGetTaskCanceledException(string.Empty);
\r
223 File.SetAttributes(filepath, FileAttributes.Normal);
\r
225 RaiseDownloadEvent(DownloadEventType.COMPLETED, fs.Position, contentLength);
\r
226 } catch (IOException ex) {
\r
227 if (cancelCalled) {
\r
228 throw new NaGetTaskCanceledException(string.Empty);
\r
230 RaiseDownloadEvent(DownloadEventType.ERROR, 0, 0);
\r
231 throw new IOException(ex.Message, ex);
\r
234 if (stopwatch != null) {
\r
242 if (File.Exists(filepath)) {
\r
243 if (response is HttpWebResponse) {
\r
244 File.SetLastWriteTime(filepath, ((HttpWebResponse) response).LastModified);
\r
245 } else if (response is FtpWebResponse) {
\r
246 File.SetLastWriteTime(filepath, ((FtpWebResponse) response).LastModified);
\r
250 if (response != null) {
\r
259 protected void RaiseDownloadEvent(DownloadEventType type, long pos, long max)
\r
261 if (DownloadEventRaised != null) {
\r
262 DownloadEventArgs e = new DownloadEventArgs(type, string.Empty, pos, max);
\r
265 case DownloadEventType.CONNECTED:
\r
266 e.TaskMessage = "接続しました";
\r
268 case DownloadEventType.STARTED:
\r
269 case DownloadEventType.DOWNLOADING:
\r
270 case DownloadEventType.COMPLETED:
\r
271 if (e.TaskProgressPercent >= 0) {
\r
272 e.TaskMessage = string.Format("{0} bytes ({1} %)", e.DownloadSize, (int) e.TaskProgressPercent);
\r
274 e.TaskMessage = string.Format("{0} bytes", e.DownloadSize);
\r
278 if (stopwatch != null && stopwatch.IsRunning && stopwatch.ElapsedMilliseconds > 3000) {
\r
279 long bpers = e.DownloadSize * 1000 / stopwatch.ElapsedMilliseconds;
\r
280 if ((e.TaskProgressPercent >= 0) && (bpers > 0)) {
\r
281 TimeSpan rest = TimeSpan.FromSeconds((max - e.DownloadSize) / bpers);
\r
282 e.TaskMessage += string.Format(" 推定残り時間:{0} ({1}/s)", rest, NaGet.Utils.FormatSize(bpers));
\r
284 e.TaskMessage += string.Format(" ({0}/s)", NaGet.Utils.FormatSize(bpers));
\r
291 DownloadEventRaised(this, e);
\r
295 public override bool Cancelable {
\r
296 get { return !(this.Done || cancelCalled); }
\r
299 public override bool Cancel()
\r
301 if (this.Done || cancelCalled) {
\r
305 cancelCalled = true;
\r
306 if (request != null) {
\r
309 } catch (WebException) {
\r
316 /// Webレスポンスからダウンロードしたファイルの名前を取得
\r
318 /// <param name="response"></param>
\r
319 /// <returns></returns>
\r
320 private string getFileNameFromWebResponse(WebResponse response)
\r
322 if (response is HttpWebResponse) {
\r
323 string contentDisposition = ((HttpWebResponse) response).Headers["Content-Disposition"];
\r
325 // TODO check license for http://www.atmarkit.co.jp/fdotnet/dotnettips/618downnoname/downnoname.html
\r
326 if (! string.IsNullOrEmpty(contentDisposition)) {
\r
327 System.Text.RegularExpressions.Regex re = new System.Text.RegularExpressions.Regex(
\r
328 @"filename\s*=\s*(?:""(?<filename>[^""]*)""|(?<filename>[^;]*))",
\r
329 System.Text.RegularExpressions.RegexOptions.IgnoreCase);
\r
331 System.Text.RegularExpressions.Match m = re.Match(contentDisposition);
\r
333 return m.Groups["filename"].Value;
\r
338 return NaGet.Utils.Url2filename(response.ResponseUri.ToString());
\r