3 using System.Net.Mime;
\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
66 public IWebProxy proxy;
\r
71 public event EventHandler<DownloadEventArgs> DownloadEventRaised;
\r
76 protected string url;
\r
81 protected string filepath;
\r
86 protected WebRequest request;
\r
89 /// レスポンスオブジェクト。応答がくるまではnullである。
\r
91 protected WebResponse response;
\r
94 /// ダウンロード要求時のキャッシュレベル。デフォルトではキャッシュ無視
\r
96 public System.Net.Cache.RequestCacheLevel CacheLevel = System.Net.Cache.RequestCacheLevel.NoCacheNoStore;
\r
98 private bool cancelCalled = false;
\r
100 private bool done = false;
\r
103 /// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名
\r
105 private string downloadedFileName = null;
\r
110 public IWebProxy Proxy {
\r
111 get { return proxy ?? NaGet.Env.WebProxy; }
\r
112 set { proxy = value; }
\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
145 public override bool Done {
\r
146 get { return done; }
\r
150 /// 現在ダウンロード処理実行中であるかのフラグ
\r
152 public override bool Running {
\r
153 get { return request != null && (!done); }
\r
159 public override void Run()
\r
161 RaiseDownloadEvent(DownloadEventType.INITED, 0, -1);
\r
164 request = WebRequest.Create(url);
\r
165 request.Proxy = this.Proxy;
\r
166 request.CachePolicy = new System.Net.Cache.RequestCachePolicy(CacheLevel);
\r
168 if (cancelCalled) {
\r
169 throw new NaGetTaskCanceledException(string.Empty);
\r
173 response = request.GetResponse();
\r
174 } catch (WebException e) {
\r
175 if (cancelCalled) { // キャンセル時
\r
176 throw new NaGetTaskCanceledException(string.Empty, e);
\r
178 throw new WebException(e.Message, e);
\r
182 if (cancelCalled) {
\r
183 throw new NaGetTaskCanceledException(string.Empty);
\r
187 downloadedFileName = getFileNameFromWebResponse(response);
\r
188 } catch (Exception) {
\r
191 if (File.Exists(filepath)) { // ファイルが存在するとき削除
\r
192 File.Delete(filepath);
\r
195 RaiseDownloadEvent(DownloadEventType.CONNECTED, 0, -1);
\r
197 using (Stream stream = response.GetResponseStream() )
\r
198 using (FileStream fs = new FileStream(filepath,
\r
200 FileAccess.Write) ) {
\r
202 File.SetAttributes(filepath, FileAttributes.Hidden);
\r
203 long contentLength = response.ContentLength;
\r
205 RaiseDownloadEvent(DownloadEventType.STARTED, 0, contentLength);
\r
207 stopwatch = new System.Diagnostics.Stopwatch();
\r
210 Timer timer = new Timer(new TimerCallback(
\r
211 delegate(object obj) {
\r
213 RaiseDownloadEvent(DownloadEventType.DOWNLOADING, fs.Position, contentLength);
\r
214 } catch (ObjectDisposedException) {
\r
216 }), null, 0, 1000);
\r
219 byte[] data = new byte[4096];
\r
221 while ((size = stream.Read(data,0,data.Length)) > 0) {
\r
222 fs.Write(data, 0, size);
\r
224 if (cancelCalled) {
\r
225 throw new NaGetTaskCanceledException(string.Empty);
\r
232 File.SetAttributes(filepath, FileAttributes.Normal);
\r
234 RaiseDownloadEvent(DownloadEventType.COMPLETED, fs.Position, contentLength);
\r
235 } catch (IOException ex) {
\r
236 if (cancelCalled) {
\r
237 throw new NaGetTaskCanceledException(string.Empty);
\r
239 RaiseDownloadEvent(DownloadEventType.ERROR, 0, 0);
\r
240 throw new IOException(ex.Message, ex);
\r
243 if (stopwatch != null) {
\r
251 if (File.Exists(filepath)) {
\r
252 if (response is HttpWebResponse) {
\r
253 File.SetLastWriteTime(filepath, ((HttpWebResponse) response).LastModified);
\r
254 } else if (response is FtpWebResponse) {
\r
255 File.SetLastWriteTime(filepath, ((FtpWebResponse) response).LastModified);
\r
259 if (response != null) {
\r
268 protected void RaiseDownloadEvent(DownloadEventType type, long pos, long max)
\r
270 if (DownloadEventRaised != null) {
\r
271 DownloadEventArgs e = new DownloadEventArgs(type, string.Empty, pos, max);
\r
274 case DownloadEventType.CONNECTED:
\r
275 e.TaskMessage = "接続しました";
\r
277 case DownloadEventType.STARTED:
\r
278 case DownloadEventType.DOWNLOADING:
\r
279 case DownloadEventType.COMPLETED:
\r
280 if (e.TaskProgressPercent >= 0) {
\r
281 e.TaskMessage = string.Format("{0} bytes ({1} %)", e.DownloadSize, (int) e.TaskProgressPercent);
\r
283 e.TaskMessage = string.Format("{0} bytes", e.DownloadSize);
\r
287 if (stopwatch != null && stopwatch.IsRunning && stopwatch.ElapsedMilliseconds > 3000) {
\r
288 long bpers = e.DownloadSize * 1000 / stopwatch.ElapsedMilliseconds;
\r
289 if ((e.TaskProgressPercent >= 0) && (bpers > 0)) {
\r
290 TimeSpan rest = TimeSpan.FromSeconds((max - e.DownloadSize) / bpers);
\r
291 e.TaskMessage += string.Format(" 推定残り時間:{0} ({1}/s)", rest, NaGet.Utils.FormatSize(bpers));
\r
293 e.TaskMessage += string.Format(" ({0}/s)", NaGet.Utils.FormatSize(bpers));
\r
300 DownloadEventRaised(this, e);
\r
307 public override bool Cancelable {
\r
308 get { return !(this.Done || cancelCalled); }
\r
312 /// ダウンロード処理をキャンセルする
\r
314 /// <returns>キャンセルに成功したときtrue</returns>
\r
315 public override bool Cancel()
\r
317 if (this.Done || cancelCalled) {
\r
321 cancelCalled = true;
\r
322 if (request != null) {
\r
325 } catch (WebException) {
\r
332 /// Webレスポンスからダウンロードしたファイルの名前を取得
\r
334 /// <remarks>Content-Dispositionヘッダから取得あるいはURLの末尾から推定します</remarks>
\r
335 /// <param name="response">レスポンスオブジェクト</param>
\r
336 /// <returns>取得したファイル名</returns>
\r
337 private string getFileNameFromWebResponse(WebResponse response)
\r
339 if (response is HttpWebResponse) {
\r
340 string contentDisposition = ((HttpWebResponse) response).Headers["Content-Disposition"];
\r
342 if (! string.IsNullOrEmpty(contentDisposition)) {
\r
344 ContentDisposition parser = new ContentDisposition(contentDisposition);
\r
345 if (! string.IsNullOrEmpty(parser.FileName)) {
\r
346 return parser.FileName;
\r
348 } catch (FormatException) {
\r
353 return NaGet.Utils.Url2filename(response.ResponseUri.ToString());
\r