OSDN Git Service

na-get-lib,起動高速化機能追加時にインストール済みソフトのバージョン認識がされていないことがあったのを修正。
[applistation/AppliStation.git] / na-get-lib / NaGet.Net / Downloader.cs
1 using System;\r
2 using System.Net;\r
3 using System.Net.Mime;\r
4 using System.IO;\r
5 using System.Threading;\r
6 using NaGet.SubCommands;\r
7 \r
8 namespace NaGet.Net\r
9 {\r
10 \r
11 /// <summary>\r
12 /// ダウンロードイベントオブジェクト\r
13 /// </summary>\r
14 public class DownloadEventArgs : NaGetEventArgs\r
15 {\r
16         /// <summary>\r
17         /// イベントの種類\r
18         /// </summary>\r
19         public DownloadEventType Type;\r
20 \r
21         /// <summary>\r
22         /// ダウンロード済みのバイト数\r
23         /// </summary>\r
24         public long DownloadSize;\r
25 \r
26         /// <summary>\r
27         /// ダウンロードする総バイト数\r
28         /// </summary>\r
29         public long MaxSize;\r
30         \r
31         /// <summary>\r
32         /// コンストラクタ\r
33         /// </summary>\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
39         {\r
40                 Type = type;\r
41                 DownloadSize = pos;\r
42                 MaxSize = max;\r
43                 \r
44                 TaskMessage = msg;\r
45                 TaskProgressPercent = (max > 0)? (100.0f * pos / max) : -1;\r
46         }\r
47 }\r
48 \r
49 /// <summary>\r
50 /// DownloadEventArgsでのイベントの種類\r
51 /// </summary>\r
52 public enum DownloadEventType {\r
53         INITED,\r
54         CONNECTED,\r
55         STARTED,\r
56         DOWNLOADING,\r
57         ERROR,\r
58         COMPLETED\r
59 }\r
60 \r
61 /// <summary>\r
62 /// ダウンロード処理を行うクラス\r
63 /// </summary>\r
64 public class Downloader : NaGetTask\r
65 {\r
66         public IWebProxy proxy;\r
67         \r
68         /// <summary>\r
69         /// イベントハンドラ\r
70         /// </summary>\r
71         public event EventHandler<DownloadEventArgs> DownloadEventRaised;\r
72         \r
73         /// <summary>\r
74         /// アクセスURL\r
75         /// </summary>\r
76         protected Uri url;\r
77         \r
78         /// <summary>\r
79         /// 保存先\r
80         /// </summary>\r
81         protected string filepath;\r
82         \r
83         /// <summary>\r
84         /// リクエストオブジェクト\r
85         /// </summary>\r
86         protected WebRequest request;\r
87         \r
88         /// <summary>\r
89         /// レスポンスオブジェクト。応答がくるまではnullである。\r
90         /// </summary>\r
91         protected WebResponse response;\r
92         \r
93         /// <summary>\r
94         /// ダウンロード要求時のキャッシュレベル。デフォルトではキャッシュ無視\r
95         /// </summary>\r
96         public System.Net.Cache.RequestCacheLevel CacheLevel = System.Net.Cache.RequestCacheLevel.NoCacheNoStore;\r
97         \r
98         private bool cancelCalled = false;\r
99         \r
100         private bool done = false;\r
101         \r
102         /// <summary>\r
103         /// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名\r
104         /// </summary>\r
105         private string downloadedFileName = null;\r
106         \r
107         /// <summary>\r
108         /// ウェブアクセスに使うプロキシ\r
109         /// </summary>\r
110         public IWebProxy Proxy {\r
111                 get { return proxy ?? NaGet.Env.WebProxy; }\r
112                 set { proxy = value; }\r
113         }\r
114         \r
115         /// <summary>\r
116         /// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名\r
117         /// </summary>\r
118         public string DownloadedFileName {\r
119                 get { return downloadedFileName; }\r
120         }\r
121         \r
122         /* ダウンロードの処理時間計測器 */\r
123         private System.Diagnostics.Stopwatch stopwatch;\r
124 \r
125         /// <summary>\r
126         /// ダウンロードを行う\r
127         /// </summary>\r
128         /// <param name="url">ダウンロードするリソースのURL</param>\r
129         /// <param name="filepath">保存先ファイルパス</param>\r
130         public void Download(string url, string filepath)\r
131         {\r
132                 this.url = new Uri(url);\r
133                 this.filepath = filepath;\r
134                 \r
135                 try {\r
136                         Run();\r
137                 } catch (NotSupportedException e) {\r
138                         throw new NotSupportedException("Not supported download location for downloader.", e);\r
139                 }\r
140         }\r
141         \r
142         /// <summary>\r
143         /// 処理が終了したか否かのフラグ\r
144         /// </summary>\r
145         public override bool Done {\r
146                 get { return done; }\r
147         }\r
148         \r
149         /// <summary>\r
150         /// 現在ダウンロード処理実行中であるかのフラグ\r
151         /// </summary>\r
152         public override bool Running {\r
153                 get { return request != null && (!done); }\r
154         }\r
155 \r
156         /// <summary>\r
157         /// ダウンロード処理を実行する\r
158         /// </summary>\r
159         public override void Run()\r
160         {\r
161                 RaiseDownloadEvent(DownloadEventType.INITED, 0, -1);\r
162                 \r
163                 try {\r
164                         request = WebRequest.Create(url);\r
165                         request.Proxy = this.Proxy;\r
166                         request.CachePolicy = new System.Net.Cache.RequestCachePolicy(CacheLevel);\r
167                         \r
168                         HttpWebRequest httpRequest = request as HttpWebRequest;\r
169                         if (httpRequest != null) {\r
170                                 httpRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;  \r
171                         }\r
172                         \r
173                         if (cancelCalled) {\r
174                                 throw new NaGetTaskCanceledException(string.Empty);\r
175                         }\r
176                         \r
177                         try {\r
178                                 response = request.GetResponse();\r
179                         } catch (WebException e) {\r
180                                 if (cancelCalled) { // キャンセル時\r
181                                         throw new NaGetTaskCanceledException(string.Empty, e);\r
182                                 } else {\r
183                                         throw new WebException(e.Message, e);\r
184                                 }\r
185                         }\r
186                         \r
187                         if (cancelCalled) {\r
188                                 throw new NaGetTaskCanceledException(string.Empty);\r
189                         }\r
190                         \r
191                         try {\r
192                                 downloadedFileName = getFileNameFromWebResponse(response);\r
193                         } catch (Exception) {\r
194                         }\r
195                         \r
196                         if (File.Exists(filepath)) { // ファイルが存在するとき削除\r
197                                 File.Delete(filepath);\r
198                         }\r
199                         \r
200                         RaiseDownloadEvent(DownloadEventType.CONNECTED, 0, -1);\r
201                         \r
202                         using (Stream stream = response.GetResponseStream() )\r
203                         using (FileStream fs = new FileStream(filepath,\r
204                                                     FileMode.Create,\r
205                                                     FileAccess.Write) ) {\r
206                                 try {\r
207                                         File.SetAttributes(filepath, FileAttributes.Hidden);\r
208                                         long contentLength = response.ContentLength;\r
209         \r
210                                         RaiseDownloadEvent(DownloadEventType.STARTED, 0, contentLength);\r
211                                         \r
212                                         stopwatch = new System.Diagnostics.Stopwatch();\r
213                                         stopwatch.Start();\r
214         \r
215                                         Timer timer = new Timer(new TimerCallback(\r
216                                                 delegate(object obj) {\r
217                                                         try {\r
218                                                                 RaiseDownloadEvent(DownloadEventType.DOWNLOADING, fs.Position, contentLength);\r
219                                                         } catch (ObjectDisposedException) {\r
220                                                         }\r
221                                                 }), null, 0, 1000);\r
222                                         \r
223                                         try {\r
224                                                 byte[] data = new byte[4096];\r
225                                                 int size = 0;\r
226                                                 while ((size = stream.Read(data,0,data.Length)) > 0) {\r
227                                                         fs.Write(data, 0, size);\r
228                                                         \r
229                                                         if (cancelCalled) {\r
230                                                                 throw new NaGetTaskCanceledException(string.Empty);\r
231                                                         }\r
232                                                 }\r
233                                         } finally {\r
234                                                 timer.Dispose();\r
235                                         }\r
236                                         \r
237                                         File.SetAttributes(filepath, FileAttributes.Normal);\r
238                                         \r
239                                         RaiseDownloadEvent(DownloadEventType.COMPLETED, fs.Position, contentLength);\r
240                                 } catch (IOException ex) {\r
241                                         if (cancelCalled) {\r
242                                                 throw new NaGetTaskCanceledException(string.Empty);\r
243                                         } else {\r
244                                                 RaiseDownloadEvent(DownloadEventType.ERROR, 0, 0);\r
245                                                 throw new IOException(ex.Message, ex);\r
246                                         }\r
247                                 } finally {\r
248                                         if (stopwatch != null) {\r
249                                                 stopwatch.Stop();\r
250                                                 stopwatch = null;\r
251                                         }\r
252                                 }\r
253                         }\r
254                         \r
255                         // 更新日を補完\r
256                         if (File.Exists(filepath)) {\r
257                                 HttpWebResponse httpResponse = response as HttpWebResponse;\r
258                                 FtpWebResponse  ftpResponse      = response as FtpWebResponse;\r
259                                 \r
260                                 if (httpResponse != null) {\r
261                                         File.SetLastWriteTime(filepath, httpResponse.LastModified);\r
262                                 } else if (ftpResponse != null) {\r
263                                         File.SetLastWriteTime(filepath, ftpResponse.LastModified);\r
264                                 }\r
265                         }\r
266                 } finally {\r
267                         if (response != null) {\r
268                                 response.Close();\r
269                         }\r
270                         \r
271                         request = null;\r
272                         done = true;\r
273                 }\r
274         }\r
275         \r
276         protected void RaiseDownloadEvent(DownloadEventType type, long pos, long max)\r
277         {\r
278                 if (DownloadEventRaised != null) {\r
279                         DownloadEventArgs e = new DownloadEventArgs(type, string.Empty, pos, max);\r
280                         \r
281                         switch (e.Type) {\r
282                                 case DownloadEventType.CONNECTED:\r
283                                         e.TaskMessage = "接続しました";\r
284                                         break;\r
285                                 case DownloadEventType.STARTED:\r
286                                 case DownloadEventType.DOWNLOADING:\r
287                                 case DownloadEventType.COMPLETED:\r
288                                         if (e.TaskProgressPercent >= 0) {\r
289                                                 e.TaskMessage = string.Format("{0} bytes ({1} %)", e.DownloadSize, (int) e.TaskProgressPercent);\r
290                                         } else {\r
291                                                 e.TaskMessage = string.Format("{0} bytes", e.DownloadSize);\r
292                                         }\r
293                                         \r
294                                         \r
295                                         if (stopwatch != null && stopwatch.IsRunning && stopwatch.ElapsedMilliseconds > 3000) {\r
296                                                 long bpers = e.DownloadSize * 1000 / stopwatch.ElapsedMilliseconds;\r
297                                                 if ((e.TaskProgressPercent >= 0) && (bpers > 0)) {\r
298                                                         TimeSpan rest = TimeSpan.FromSeconds((max - e.DownloadSize) / bpers);\r
299                                                         e.TaskMessage += string.Format(" 推定残り時間:{0} ({1}/s)", rest, NaGet.Utils.FormatSize(bpers));\r
300                                                 } else {\r
301                                                         e.TaskMessage += string.Format(" ({0}/s)", NaGet.Utils.FormatSize(bpers));\r
302                                                 }\r
303                                         }\r
304                                         \r
305                                         break;\r
306                         }\r
307                         \r
308                         DownloadEventRaised(this, e);\r
309                 }\r
310         }\r
311         \r
312         /// <summary>\r
313         /// キャンセル可能かを返す\r
314         /// </summary>\r
315         public override bool Cancelable {\r
316                 get { return !(this.Done || cancelCalled); }\r
317         }\r
318         \r
319         /// <summary>\r
320         /// ダウンロード処理をキャンセルする\r
321         /// </summary>\r
322         /// <returns>キャンセルに成功したときtrue</returns>\r
323         public override bool Cancel()\r
324         {\r
325                 if (this.Done || cancelCalled) {\r
326                         return false;\r
327                 }\r
328                 \r
329                 cancelCalled = true;\r
330                 if (request != null) {\r
331                         try {\r
332                                 request.Abort();\r
333                         } catch (WebException) {\r
334                         }\r
335                 }\r
336                 return true;\r
337         }\r
338         \r
339         /// <summary>\r
340         /// Webレスポンスからダウンロードしたファイルの名前を取得\r
341         /// </summary>\r
342         /// <remarks>Content-Dispositionヘッダから取得あるいはURLの末尾から推定します</remarks>\r
343         /// <param name="response">レスポンスオブジェクト</param>\r
344         /// <returns>取得したファイル名</returns>\r
345         private static string getFileNameFromWebResponse(WebResponse response)\r
346         {\r
347                 HttpWebResponse httpresp = response as HttpWebResponse;\r
348                 if (httpresp != null) {\r
349                         string contentDisposition = httpresp.Headers["Content-Disposition"];\r
350                         \r
351                         if (! string.IsNullOrEmpty(contentDisposition)) {\r
352                                 try {\r
353                                         ContentDisposition parser = new ContentDisposition(contentDisposition);\r
354                                         if (! string.IsNullOrEmpty(parser.FileName)) {\r
355                                                 return parser.FileName;\r
356                                         }\r
357                                 } catch (FormatException) {\r
358                                 }\r
359                         }\r
360                 }\r
361                 \r
362                 return NaGet.Utils.Url2filename(response.ResponseUri);\r
363         }\r
364 }\r
365 \r
366 }\r
367 \r
368 \r