2 using System.Diagnostics;
6 using System.Threading;
9 namespace NaGet.SubCommands.SubTask
14 public class DownloadSubTask : NaGetSubTask
24 protected IWebProxy proxy;
29 protected string filepath;
34 protected WebRequest request;
37 /// レスポンスオブジェクト。応答がくるまではnullである。
39 protected WebResponse response;
42 /// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名
44 private string downloadedFileName = null;
47 /// ダウンロード要求時のキャッシュレベル。デフォルトではキャッシュ無視
49 public System.Net.Cache.RequestCacheLevel CacheLevel = System.Net.Cache.RequestCacheLevel.NoCacheNoStore;
54 private bool cancelCalled = false;
59 /// <param name="url">ダウンロード先URL</param>
60 /// <param name="filepath">保存ファイルパス</param>
61 /// <param name="proxy">プロキシ</param>
62 public DownloadSubTask(Uri url, string filepath, IWebProxy proxy)
65 this.filepath = filepath;
70 this.downloadedFileName = null;
73 public DownloadSubTask(string url, string filepath, IWebProxy proxy)
74 : this(new Uri(url), filepath, proxy)
81 /// <param name="url">ダウンロード先URL</param>
82 /// <param name="filepath">保存ファイルパス</param>
83 public DownloadSubTask(Uri url, string filepath)
84 : this(url, filepath, NaGet.Env.WebProxy)
88 public DownloadSubTask(string url, string filepath)
89 : this(new Uri(url), filepath, NaGet.Env.WebProxy)
94 /// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名
96 public string DownloadedFileName {
97 get { return downloadedFileName; }
103 public override bool Cancelable {
104 get { return !this.cancelCalled; }
110 /// <returns>キャンセルに成功したときtrue</returns>
111 public override bool Cancel()
113 if (! this.cancelCalled && ! this.Done) {
114 this.cancelCalled = true;
115 if (request != null) {
118 } catch (WebException) {
127 public override void Run()
130 RaiseTaskSetEvent(TaskEventType.STARTED, string.Format("ダウンロード:{0}", this.url), 0);
134 runHandleCancelTrigger();
136 RaiseTaskSetEvent(TaskEventType.PING, string.Format("接続中...{0}", this.url.Host), -1);
138 runAcquireResponse();
139 runHandleCancelTrigger();
143 runHandleCancelTrigger();
147 RaiseTaskSetEvent(TaskEventType.COMPLETED, "ダウンロード終了", 100);
148 } catch (System.Net.WebException e) {
149 if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable()) {
150 RaiseTaskSetEvent(TaskEventType.WARNING, "ネットワークに接続されていません。", -1);
152 RaiseTaskSetEvent(TaskEventType.WARNING, "ネットワークに接続できませんでした。ネットワークが切断されているか、ファイアウォールによって遮断された可能性があります。", -1);
154 throw new System.Net.WebException(e.Message, e);
156 runReleaseResponse();
167 /// キャンセルされたかを確認して、キャンセル要求があるのならば TaskCanceledException を投げる
169 private void runHandleCancelTrigger()
171 if (this.cancelCalled) {
172 throw new TaskCanceledException(string.Empty);
179 private void runBuildRequest()
181 request = WebRequest.Create(url);
182 request.Proxy = this.proxy;
183 request.CachePolicy = new System.Net.Cache.RequestCachePolicy(CacheLevel);
185 HttpWebRequest httpRequest = request as HttpWebRequest;
186 if (httpRequest != null) {
187 httpRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
189 httpRequest.UserAgent = "AppliStation/1.3";
196 private void runAcquireResponse()
199 response = request.GetResponse();
200 } catch (WebException e) {
201 if (cancelCalled) { // キャンセル時
202 throw new TaskCanceledException(string.Empty, e);
204 throw new WebException(e.Message, e);
212 private void runPrepareFile()
215 downloadedFileName = getFileNameFromWebResponse(response);
216 } catch (Exception) {
219 if (File.Exists(filepath)) { // ファイルが存在するとき削除
220 File.Delete(filepath);
224 private void runDownloadToFile()
226 Stopwatch stopwatch = new Stopwatch();
228 using (Stream stream = response.GetResponseStream() )
229 using (FileStream fs = new FileStream(filepath,
231 FileAccess.Write) ) {
233 File.SetAttributes(filepath, FileAttributes.Hidden);
234 long contentLength = response.ContentLength;
237 RaiseDownloadProgressTaskSetEvent(0, contentLength, 0);
239 Timer timer = new Timer(new TimerCallback(
240 delegate(object obj) {
242 RaiseDownloadProgressTaskSetEvent(fs.Position, contentLength, stopwatch.ElapsedMilliseconds);
243 } catch (ObjectDisposedException) {
248 byte[] data = new byte[4096];
250 while ((size = stream.Read(data,0,data.Length)) > 0) {
251 fs.Write(data, 0, size);
254 throw new TaskCanceledException(string.Empty);
261 File.SetAttributes(filepath, FileAttributes.Normal);
262 } catch (IOException ex) {
264 throw new TaskCanceledException(string.Empty);
266 throw new IOException(ex.Message, ex);
269 if (stopwatch != null) {
277 private void runPostprocess()
280 if (File.Exists(filepath)) {
281 HttpWebResponse httpResponse = response as HttpWebResponse;
282 FtpWebResponse ftpResponse = response as FtpWebResponse;
284 if (httpResponse != null) {
285 File.SetLastWriteTime(filepath, httpResponse.LastModified);
286 } else if (ftpResponse != null) {
287 File.SetLastWriteTime(filepath, ftpResponse.LastModified);
295 private void runReleaseResponse()
297 if (response != null) {
303 /// Webレスポンスからダウンロードしたファイルの名前を取得
305 /// <remarks>Content-Dispositionヘッダから取得あるいはURLの末尾から推定します</remarks>
306 /// <param name="response">レスポンスオブジェクト</param>
307 /// <returns>取得したファイル名</returns>
308 private static string getFileNameFromWebResponse(WebResponse response)
310 HttpWebResponse httpresp = response as HttpWebResponse;
311 if (httpresp != null) {
312 string contentDisposition = httpresp.Headers["Content-Disposition"];
314 if (! string.IsNullOrEmpty(contentDisposition)) {
316 ContentDisposition parser = new ContentDisposition(contentDisposition);
317 if (! string.IsNullOrEmpty(parser.FileName)) {
318 return parser.FileName;
320 } catch (FormatException) {
325 return NaGet.Utils.Url2filename(response.ResponseUri);
331 /// <param name="downloadsize">現在ダウンロード済みのサイズ</param>
332 /// <param name="filesize">全体のファイルサイズ。不明なときはゼロを指定。</param>
333 /// <param name="elapsedms">ダウンロード開始からの時間をms単位で。</param>
334 protected virtual void RaiseDownloadProgressTaskSetEvent(long downloadsize, long filesize, long elapsedms)
337 TimeSpan eta = new TimeSpan(0);
342 percent = 100 * ((float) downloadsize) / ((float) filesize);
347 byteps = 1000 * downloadsize / elapsedms;
348 if (filesize > 0 && byteps > 0) {
349 eta = TimeSpan.FromSeconds((filesize - downloadsize) / byteps);
353 System.Text.StringBuilder msgbuilder = new System.Text.StringBuilder();
354 msgbuilder.AppendFormat("{0} bytes", downloadsize);
356 msgbuilder.AppendFormat(" ({0:F2}%)", percent);
359 msgbuilder.AppendFormat(" ETA {0}", eta);
362 msgbuilder.AppendFormat(" ({0}/s)", NaGet.Utils.FormatSize(byteps));
365 RaiseTaskSetEvent(TaskEventType.PING, msgbuilder.ToString(), percent);