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;
76 /// <param name="url">ダウンロード先URL</param>
77 /// <param name="filepath">保存ファイルパス</param>
78 public DownloadSubTask(Uri url, string filepath)
79 : this(url, filepath, NaGet.Env.WebProxy)
84 /// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名
86 public string DownloadedFileName {
87 get { return downloadedFileName; }
90 public override void Run()
93 RaiseTaskSetEvent(TaskEventType.STARTED, string.Format("ダウンロード:{0}", this.url), 0);
97 runHandleCancelTrigger();
99 RaiseTaskSetEvent(TaskEventType.PING, string.Format("接続中...{0}", this.url.Host), -1);
101 runAcquireResponse();
102 runHandleCancelTrigger();
106 runHandleCancelTrigger();
110 runReleaseResponse();
116 RaiseTaskSetEvent(TaskEventType.COMPLETED, "ダウンロード終了", 100);
122 /// キャンセルされたかを確認して、キャンセル要求があるのならば TaskCanceledException を投げる
124 private void runHandleCancelTrigger()
126 if (this.cancelCalled) {
127 throw new TaskCanceledException(string.Empty);
134 private void runBuildRequest()
136 request = WebRequest.Create(url);
137 request.Proxy = this.proxy;
138 request.CachePolicy = new System.Net.Cache.RequestCachePolicy(CacheLevel);
140 HttpWebRequest httpRequest = request as HttpWebRequest;
141 if (httpRequest != null) {
142 httpRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
144 httpRequest.UserAgent = "AppliStation/1.3";
151 private void runAcquireResponse()
154 response = request.GetResponse();
155 } catch (WebException e) {
156 if (cancelCalled) { // キャンセル時
157 throw new TaskCanceledException(string.Empty, e);
159 throw new WebException(e.Message, e);
167 private void runPrepareFile()
170 downloadedFileName = getFileNameFromWebResponse(response);
171 } catch (Exception) {
174 if (File.Exists(filepath)) { // ファイルが存在するとき削除
175 File.Delete(filepath);
179 private void runDownloadToFile()
181 Stopwatch stopwatch = new Stopwatch();
183 using (Stream stream = response.GetResponseStream() )
184 using (FileStream fs = new FileStream(filepath,
186 FileAccess.Write) ) {
188 File.SetAttributes(filepath, FileAttributes.Hidden);
189 long contentLength = response.ContentLength;
192 RaiseDownloadProgressTaskSetEvent(0, contentLength, 0);
194 Timer timer = new Timer(new TimerCallback(
195 delegate(object obj) {
197 RaiseDownloadProgressTaskSetEvent(fs.Position, contentLength, stopwatch.ElapsedMilliseconds);
198 } catch (ObjectDisposedException) {
203 byte[] data = new byte[4096];
205 while ((size = stream.Read(data,0,data.Length)) > 0) {
206 fs.Write(data, 0, size);
209 throw new TaskCanceledException(string.Empty);
216 File.SetAttributes(filepath, FileAttributes.Normal);
217 } catch (IOException ex) {
219 throw new TaskCanceledException(string.Empty);
221 throw new IOException(ex.Message, ex);
224 if (stopwatch != null) {
232 private void runPostprocess()
235 if (File.Exists(filepath)) {
236 HttpWebResponse httpResponse = response as HttpWebResponse;
237 FtpWebResponse ftpResponse = response as FtpWebResponse;
239 if (httpResponse != null) {
240 File.SetLastWriteTime(filepath, httpResponse.LastModified);
241 } else if (ftpResponse != null) {
242 File.SetLastWriteTime(filepath, ftpResponse.LastModified);
250 private void runReleaseResponse()
252 if (response != null) {
260 /// <returns>キャンセルに成功したときtrue</returns>
261 public override bool Cancel()
263 if (! this.cancelCalled && ! this.Done) {
264 this.cancelCalled = true;
265 if (request != null) {
268 } catch (WebException) {
278 /// Webレスポンスからダウンロードしたファイルの名前を取得
280 /// <remarks>Content-Dispositionヘッダから取得あるいはURLの末尾から推定します</remarks>
281 /// <param name="response">レスポンスオブジェクト</param>
282 /// <returns>取得したファイル名</returns>
283 private static string getFileNameFromWebResponse(WebResponse response)
285 HttpWebResponse httpresp = response as HttpWebResponse;
286 if (httpresp != null) {
287 string contentDisposition = httpresp.Headers["Content-Disposition"];
289 if (! string.IsNullOrEmpty(contentDisposition)) {
291 ContentDisposition parser = new ContentDisposition(contentDisposition);
292 if (! string.IsNullOrEmpty(parser.FileName)) {
293 return parser.FileName;
295 } catch (FormatException) {
300 return NaGet.Utils.Url2filename(response.ResponseUri);
306 /// <param name="downloadsize">現在ダウンロード済みのサイズ</param>
307 /// <param name="filesize">全体のファイルサイズ。不明なときはゼロを指定。</param>
308 /// <param name="elapsedms">ダウンロード開始からの時間をms単位で。</param>
309 protected virtual void RaiseDownloadProgressTaskSetEvent(long downloadsize, long filesize, long elapsedms)
312 TimeSpan eta = new TimeSpan(0);
317 percent = 100 * ((float) downloadsize) / ((float) filesize);
322 byteps = 1000 * downloadsize / elapsedms;
324 eta = TimeSpan.FromSeconds((filesize - downloadsize) / byteps);
328 System.Text.StringBuilder msgbuilder = new System.Text.StringBuilder();
329 msgbuilder.AppendFormat("{0} bytes", downloadsize);
331 msgbuilder.AppendFormat(" ({0}%)", percent);
334 msgbuilder.AppendFormat(" ETA {0}", eta);
337 msgbuilder.AppendFormat(" ({0}/s)", NaGet.Utils.FormatSize(byteps));
340 RaiseTaskSetEvent(TaskEventType.PING, msgbuilder.ToString(), percent);