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 runReleaseResponse();
153 RaiseTaskSetEvent(TaskEventType.COMPLETED, "ダウンロード終了", 100);
159 /// キャンセルされたかを確認して、キャンセル要求があるのならば TaskCanceledException を投げる
161 private void runHandleCancelTrigger()
163 if (this.cancelCalled) {
164 throw new TaskCanceledException(string.Empty);
171 private void runBuildRequest()
173 request = WebRequest.Create(url);
174 request.Proxy = this.proxy;
175 request.CachePolicy = new System.Net.Cache.RequestCachePolicy(CacheLevel);
177 HttpWebRequest httpRequest = request as HttpWebRequest;
178 if (httpRequest != null) {
179 httpRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
181 httpRequest.UserAgent = "AppliStation/1.3";
188 private void runAcquireResponse()
191 response = request.GetResponse();
192 } catch (WebException e) {
193 if (cancelCalled) { // キャンセル時
194 throw new TaskCanceledException(string.Empty, e);
196 throw new WebException(e.Message, e);
204 private void runPrepareFile()
207 downloadedFileName = getFileNameFromWebResponse(response);
208 } catch (Exception) {
211 if (File.Exists(filepath)) { // ファイルが存在するとき削除
212 File.Delete(filepath);
216 private void runDownloadToFile()
218 Stopwatch stopwatch = new Stopwatch();
220 using (Stream stream = response.GetResponseStream() )
221 using (FileStream fs = new FileStream(filepath,
223 FileAccess.Write) ) {
225 File.SetAttributes(filepath, FileAttributes.Hidden);
226 long contentLength = response.ContentLength;
229 RaiseDownloadProgressTaskSetEvent(0, contentLength, 0);
231 Timer timer = new Timer(new TimerCallback(
232 delegate(object obj) {
234 RaiseDownloadProgressTaskSetEvent(fs.Position, contentLength, stopwatch.ElapsedMilliseconds);
235 } catch (ObjectDisposedException) {
240 byte[] data = new byte[4096];
242 while ((size = stream.Read(data,0,data.Length)) > 0) {
243 fs.Write(data, 0, size);
246 throw new TaskCanceledException(string.Empty);
253 File.SetAttributes(filepath, FileAttributes.Normal);
254 } catch (IOException ex) {
256 throw new TaskCanceledException(string.Empty);
258 throw new IOException(ex.Message, ex);
261 if (stopwatch != null) {
269 private void runPostprocess()
272 if (File.Exists(filepath)) {
273 HttpWebResponse httpResponse = response as HttpWebResponse;
274 FtpWebResponse ftpResponse = response as FtpWebResponse;
276 if (httpResponse != null) {
277 File.SetLastWriteTime(filepath, httpResponse.LastModified);
278 } else if (ftpResponse != null) {
279 File.SetLastWriteTime(filepath, ftpResponse.LastModified);
287 private void runReleaseResponse()
289 if (response != null) {
295 /// Webレスポンスからダウンロードしたファイルの名前を取得
297 /// <remarks>Content-Dispositionヘッダから取得あるいはURLの末尾から推定します</remarks>
298 /// <param name="response">レスポンスオブジェクト</param>
299 /// <returns>取得したファイル名</returns>
300 private static string getFileNameFromWebResponse(WebResponse response)
302 HttpWebResponse httpresp = response as HttpWebResponse;
303 if (httpresp != null) {
304 string contentDisposition = httpresp.Headers["Content-Disposition"];
306 if (! string.IsNullOrEmpty(contentDisposition)) {
308 ContentDisposition parser = new ContentDisposition(contentDisposition);
309 if (! string.IsNullOrEmpty(parser.FileName)) {
310 return parser.FileName;
312 } catch (FormatException) {
317 return NaGet.Utils.Url2filename(response.ResponseUri);
323 /// <param name="downloadsize">現在ダウンロード済みのサイズ</param>
324 /// <param name="filesize">全体のファイルサイズ。不明なときはゼロを指定。</param>
325 /// <param name="elapsedms">ダウンロード開始からの時間をms単位で。</param>
326 protected virtual void RaiseDownloadProgressTaskSetEvent(long downloadsize, long filesize, long elapsedms)
329 TimeSpan eta = new TimeSpan(0);
334 percent = 100 * ((float) downloadsize) / ((float) filesize);
339 byteps = 1000 * downloadsize / elapsedms;
341 eta = TimeSpan.FromSeconds((filesize - downloadsize) / byteps);
345 System.Text.StringBuilder msgbuilder = new System.Text.StringBuilder();
346 msgbuilder.AppendFormat("{0} bytes", downloadsize);
348 msgbuilder.AppendFormat(" ({0}%)", percent);
351 msgbuilder.AppendFormat(" ETA {0}", eta);
354 msgbuilder.AppendFormat(" ({0}/s)", NaGet.Utils.FormatSize(byteps));
357 RaiseTaskSetEvent(TaskEventType.PING, msgbuilder.ToString(), percent);