public System.Net.Cache.RequestCacheLevel CacheLevel = System.Net.Cache.RequestCacheLevel.NoCacheNoStore;
/// <summary>
+ /// ダウンロード時に downloadedFileName に改名するか否か。
+ /// </summary>
+ protected bool enableChangeFileName = false;
+
+ /// <summary>
/// キャンセルが呼ばれたか否か。
/// </summary>
private bool cancelCalled = false;
/// <summary>
+ /// ダウンロード中のファイル名につける接尾辞
+ /// </summary>
+ private static readonly string PartialFilePostfix = ".part";
+
+ /// <summary>
/// コンストラクタ
/// </summary>
/// <param name="url">ダウンロード先URL</param>
this.downloadedFileName = null;
}
+ public DownloadSubTask(string url, string filepath, IWebProxy proxy)
+ : this(new Uri(url), filepath, proxy)
+ {
+ }
+
/// <summary>
/// コンストラクタ
/// </summary>
: this(url, filepath, NaGet.Env.WebProxy)
{
}
+
+ public DownloadSubTask(string url, string filepath)
+ : this(new Uri(url), filepath, NaGet.Env.WebProxy)
+ {
+ }
/// <summary>
/// ダウンロード時にHTTPヘッダなどから取得した本来のファイル名
}
/// <summary>
+ /// 保存先ファイル名を本来のファイル名に変えるか。
+ /// </summary>
+ public bool EnableChangeFileName {
+ get { return enableChangeFileName; }
+ set { enableChangeFileName = value; }
+ }
+
+ /// <summary>
+ /// 保存ファイル。
+ /// </summary>
+ public string Filepath {
+ get { return filepath; }
+ }
+
+ /// <summary>
/// キャンセル可能
/// </summary>
public override bool Cancelable {
runHandleCancelTrigger();
runPostprocess();
+
+ RaiseTaskSetEvent(TaskEventType.COMPLETED, "ダウンロード終了", 100);
+ } catch (System.Net.WebException e) {
+ if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable()) {
+ RaiseTaskSetEvent(TaskEventType.WARNING, "ネットワークに接続されていません。", -1);
+ } else {
+ RaiseTaskSetEvent(TaskEventType.WARNING, "ネットワークに接続できませんでした。ネットワークが切断されているか、ファイアウォールによって遮断された可能性があります。", -1);
+ }
+ throw new System.Net.WebException(e.Message, e);
} finally {
runReleaseResponse();
NotifyCancelled();
} else {
NotifyCompleted();
- RaiseTaskSetEvent(TaskEventType.COMPLETED, "ダウンロード終了", 100);
}
}
}
HttpWebRequest httpRequest = request as HttpWebRequest;
if (httpRequest != null) {
- httpRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
- // TODO User-Agent
- httpRequest.UserAgent = "AppliStation/1.3";
+ httpRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
+ httpRequest.UserAgent = NaGet.Env.UserAgentString;
}
}
} catch (Exception) {
}
- if (File.Exists(filepath)) { // ファイルが存在するとき削除
+ // パス名を変えるときは、HTTPヘッダから取得したファイル名に変更する。
+ if (enableChangeFileName && (!string.IsNullOrEmpty(downloadedFileName))) {
+ filepath = Path.Combine(Path.GetDirectoryName(filepath), downloadedFileName);
+ }
+
+ // ファイルが存在するとき削除
+ if (File.Exists(filepath)) {
File.Delete(filepath);
}
+ // 部分ファイルが存在するときも削除 TODO レジューム処理
+ if (File.Exists(filepath + PartialFilePostfix)) {
+ File.Delete(filepath + PartialFilePostfix);
+ }
}
private void runDownloadToFile()
{
Stopwatch stopwatch = new Stopwatch();
+ string partialFilepath = filepath + PartialFilePostfix;
using (Stream stream = response.GetResponseStream() )
- using (FileStream fs = new FileStream(filepath,
+ using (FileStream fs = new FileStream(partialFilepath,
FileMode.Create,
FileAccess.Write) ) {
+
try {
- File.SetAttributes(filepath, FileAttributes.Hidden);
+ File.SetAttributes(partialFilepath, FileAttributes.Hidden);
long contentLength = response.ContentLength;
stopwatch.Start();
} finally {
timer.Dispose();
}
-
- File.SetAttributes(filepath, FileAttributes.Normal);
} catch (IOException ex) {
if (cancelCalled) {
throw new TaskCanceledException(string.Empty);
}
}
}
+
+ if (File.Exists(partialFilepath)) {
+ File.Move(partialFilepath, filepath);
+ File.SetAttributes(filepath, FileAttributes.Normal);
+ }
}
private void runPostprocess()
- {
+ {
// 更新日を補完
if (File.Exists(filepath)) {
HttpWebResponse httpResponse = response as HttpWebResponse;
string contentDisposition = httpresp.Headers["Content-Disposition"];
if (! string.IsNullOrEmpty(contentDisposition)) {
+ // おかしな Content-Disposition ヘッダ向け
+ //(Content-Disposition: filename=\"...\" に attachment; を補って RFC1806 に準拠させる)
+ if (System.Text.RegularExpressions.Regex.IsMatch(contentDisposition, @" *filename=", System.Text.RegularExpressions.RegexOptions.IgnoreCase)) {
+ contentDisposition = "attachment; " + contentDisposition;
+ }
+ // "atachment;filename=\""のようにセミコロンの後ろにスペースがない場合、それを補充する
+ if (System.Text.RegularExpressions.Regex.IsMatch(contentDisposition, @";[^ ]", System.Text.RegularExpressions.RegexOptions.IgnoreCase)) {
+ contentDisposition = string.Join("; ", System.Text.RegularExpressions.Regex.Split(contentDisposition, @"; *"));
+ }
+
try {
ContentDisposition parser = new ContentDisposition(contentDisposition);
if (! string.IsNullOrEmpty(parser.FileName)) {
// スループット・残り時間の算出
if (elapsedms > 0) {
byteps = 1000 * downloadsize / elapsedms;
- if (filesize > 0) {
+ if (filesize > 0 && byteps > 0) {
eta = TimeSpan.FromSeconds((filesize - downloadsize) / byteps);
}
}
System.Text.StringBuilder msgbuilder = new System.Text.StringBuilder();
msgbuilder.AppendFormat("{0} bytes", downloadsize);
if (percent > 0) {
- msgbuilder.AppendFormat(" ({0}%)", percent);
+ msgbuilder.AppendFormat(" ({0:F2}%)", percent);
}
if (eta.Ticks > 0) {
msgbuilder.AppendFormat(" ETA {0}", eta);