Downloader.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Net;
  5. using System.ComponentModel;
  6. using System.Xml.Serialization;
  7. using System.IO;
  8. using System.Threading;
  9. using System.Collections.Specialized;
  10. namespace ProjectBase.Data.Update
  11. {
  12. /// <summary>
  13. /// 下载错误事件数据
  14. /// </summary>
  15. public class DownloadErrorEventArgs : EventArgs
  16. {
  17. public Exception Error { get; set; }
  18. public Manifest Manifest { get; set; }
  19. }
  20. /// <summary>
  21. /// 下载进度事件数据
  22. /// </summary>
  23. public class DownloadProgressEventArgs : ProgressChangedEventArgs
  24. {
  25. public DownloadProgressEventArgs(int progressPercentage, object userState)
  26. : base(progressPercentage,userState)
  27. { }
  28. /// <summary>
  29. /// 当前下载的文件名
  30. /// </summary>
  31. public string FileName { get; set; }
  32. /// <summary>
  33. /// 获取收到的字节数。
  34. /// </summary>
  35. public long BytesReceived { get; set; }
  36. /// <summary>
  37. /// 获取 System.Net.WebClient 数据下载操作中的字节总数。
  38. /// </summary>
  39. public long TotalBytesToReceive { get; set; }
  40. }
  41. /// <summary>
  42. /// 下载完成事件数据
  43. /// </summary>
  44. public class DownloadCompleteEventArgs : AsyncCompletedEventArgs
  45. {
  46. public DownloadCompleteEventArgs(Exception error, bool cancelled, object userState)
  47. : base(error, cancelled, userState)
  48. {
  49. }
  50. public Manifest Manifest { get; set; }
  51. }
  52. /// <summary>
  53. /// 服务器文件下载类
  54. /// </summary>
  55. public class DownloadClass : Component
  56. {
  57. #region 变量定义
  58. private WebClient webClient = new WebClient();
  59. private Manifest manifest;
  60. private int fileCount = 0; // 文件数量
  61. private long fileSize = 0; // 每个文件大小
  62. private bool cancel = false;
  63. private string tempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../temp");
  64. private HybridDictionary userStateToLifetime = new HybridDictionary();
  65. private object defaultTaskId = new object();
  66. private delegate void WorkerEventHandler(AsyncOperation asyncOp);
  67. private System.ComponentModel.Container components = null;
  68. private SendOrPostCallback onProgressReportDelegate;
  69. private SendOrPostCallback onCompletedDelegate;
  70. private AsyncOperation current;
  71. #endregion
  72. #region 事件
  73. /// <summary>
  74. /// 下载进度
  75. /// </summary>
  76. public event EventHandler<DownloadProgressEventArgs> DownloadProgressChanged;
  77. /// <summary>
  78. /// 下载完成事件
  79. /// </summary>
  80. public event EventHandler<DownloadCompleteEventArgs> DownloadCompleted;
  81. /// <summary>
  82. /// 下载错误触发的事件
  83. /// </summary>
  84. public event EventHandler<DownloadErrorEventArgs> DownloadError;
  85. #endregion
  86. #region 构造及析构
  87. public DownloadClass(IContainer container)
  88. {
  89. container.Add(this);
  90. InitializeComponent();
  91. InitializeDelegates();
  92. }
  93. public DownloadClass()
  94. {
  95. InitializeComponent();
  96. InitializeDelegates();
  97. }
  98. /// <summary>
  99. /// 初始化代理
  100. /// </summary>
  101. protected virtual void InitializeDelegates()
  102. {
  103. onProgressReportDelegate = new SendOrPostCallback(ReportProgress);
  104. onCompletedDelegate = new SendOrPostCallback(DoDownloadCompleted);
  105. }
  106. private void InitializeComponent()
  107. {
  108. components = new System.ComponentModel.Container();
  109. }
  110. protected override void Dispose(bool disposing)
  111. {
  112. if (disposing)
  113. {
  114. if (components != null)
  115. {
  116. components.Dispose();
  117. }
  118. }
  119. base.Dispose(disposing);
  120. }
  121. #endregion
  122. /// <summary>
  123. /// 触发下载进度事件
  124. /// </summary>
  125. /// <param name="e"></param>
  126. protected virtual void OnDownloadProgressChanged(DownloadProgressEventArgs e)
  127. {
  128. if (DownloadProgressChanged != null)
  129. {
  130. DownloadProgressChanged(this, e);
  131. }
  132. }
  133. /// <summary>
  134. /// 触发下载完成事件
  135. /// </summary>
  136. /// <param name="e"></param>
  137. protected virtual void OnDownloadCompleted(DownloadCompleteEventArgs e)
  138. {
  139. if (DownloadCompleted != null)
  140. {
  141. DownloadCompleted(this, e);
  142. }
  143. }
  144. /// <summary>
  145. /// 触发下载错误事件
  146. /// </summary>
  147. /// <param name="e"></param>
  148. protected virtual void OnDownloadError(DownloadErrorEventArgs e)
  149. {
  150. if (DownloadError != null)
  151. {
  152. DownloadError(this, e);
  153. }
  154. }
  155. /// <summary>
  156. /// 下载文字保存的临时目录
  157. /// </summary>
  158. public string TempPath
  159. {
  160. get
  161. {
  162. return tempPath;
  163. }
  164. set
  165. {
  166. tempPath = value;
  167. }
  168. }
  169. /// <summary>
  170. /// 同步下载
  171. /// </summary>
  172. /// <param name="manifest">文件下载清单</param>
  173. public void Download(Manifest manifest)
  174. {
  175. Init(manifest);
  176. foreach (var file in manifest.ManifestFiles.Files)
  177. {
  178. string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source);
  179. string clientFileName = Path.Combine(tempPath, file.Source);
  180. Uri uri = new Uri(serverFileName);
  181. if (!Directory.Exists(Path.GetDirectoryName(clientFileName)))
  182. {
  183. Directory.CreateDirectory(Path.GetDirectoryName(clientFileName));
  184. }
  185. webClient.DownloadFile(uri, clientFileName);
  186. }
  187. }
  188. /// <summary>
  189. /// 异步下载
  190. /// </summary>
  191. /// <param name="manifest">文件下载清单</param>
  192. public void DownloadAsync(Manifest manifest)
  193. {
  194. Init(manifest);
  195. DownloadAsync(manifest,defaultTaskId);
  196. }
  197. /// <summary>
  198. /// 异步下载并指定任务Id
  199. /// </summary>
  200. /// <param name="manifest">文件下载清单</param>
  201. /// <param name="taskId">任务Id</param>
  202. public void DownloadAsync(Manifest manifest,object taskId)
  203. {
  204. AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);
  205. lock (userStateToLifetime.SyncRoot)
  206. {
  207. if (userStateToLifetime.Contains(taskId))
  208. {
  209. throw new ArgumentException("参数taskId必须是唯一的", "taskId");
  210. }
  211. userStateToLifetime[taskId] = asyncOp;
  212. }
  213. WorkerEventHandler workerDelegate = new WorkerEventHandler(DownloadWorker);
  214. workerDelegate.BeginInvoke(asyncOp, null, null);
  215. }
  216. private void Init(Manifest mainfest)
  217. {
  218. this.manifest = mainfest;
  219. webClient.BaseAddress = manifest.ManifestFiles.BaseUrl;
  220. webClient.Credentials = new NetworkCredential(mainfest.FtpName, mainfest.FtpPsswd);
  221. webClient.Encoding = Encoding.UTF8;
  222. }
  223. /// <summary>
  224. /// 异步下载方法
  225. /// </summary>
  226. /// <param name="asyncOp"></param>
  227. private void DownloadWorker(AsyncOperation asyncOp)
  228. {
  229. current = asyncOp;
  230. if (!TaskCanceled(asyncOp.UserSuppliedState))
  231. {
  232. try
  233. {
  234. webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(webClient_DownloadFileCompleted);
  235. webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
  236. foreach (var file in manifest.ManifestFiles.Files)
  237. {
  238. string serverFileName = Path.Combine(manifest.ManifestFiles.BaseUrl, file.Source);
  239. string clientFileName = Path.Combine(tempPath, file.Source);
  240. Uri uri = new Uri(serverFileName);
  241. if (!Directory.Exists(Path.GetDirectoryName(clientFileName)))
  242. {
  243. Directory.CreateDirectory(Path.GetDirectoryName(clientFileName));
  244. }
  245. WebRequest sizeRequest = WebRequest.Create(serverFileName);
  246. sizeRequest.Credentials = new NetworkCredential(manifest.FtpName, manifest.FtpPsswd); ;
  247. sizeRequest.Method = WebRequestMethods.Ftp.GetFileSize;
  248. fileSize += (int)sizeRequest.GetResponse().ContentLength;
  249. while (webClient.IsBusy)
  250. {
  251. //阻塞异步下载
  252. }
  253. if (!cancel)
  254. {
  255. webClient.DownloadFileAsync(uri, clientFileName, file.Source);
  256. }
  257. }
  258. }
  259. catch (Exception ex)
  260. {
  261. DownloadErrorEventArgs e = new DownloadErrorEventArgs();
  262. e.Error = ex;
  263. e.Manifest = manifest;
  264. OnDownloadError(e);
  265. }
  266. }
  267. }
  268. /// <summary>
  269. /// 异步完成方法
  270. /// </summary>
  271. /// <param name="exception">异常数据</param>
  272. /// <param name="canceled">是否取消</param>
  273. /// <param name="asyncOp"></param>
  274. private void CompletionMethod(Exception exception, bool canceled, AsyncOperation asyncOp)
  275. {
  276. if (!canceled)
  277. {
  278. lock (userStateToLifetime.SyncRoot)
  279. {
  280. userStateToLifetime.Remove(asyncOp.UserSuppliedState);
  281. }
  282. }
  283. DownloadCompleteEventArgs e = new DownloadCompleteEventArgs(exception, canceled, asyncOp.UserSuppliedState);
  284. e.Manifest = manifest;
  285. asyncOp.PostOperationCompleted(onCompletedDelegate, e);
  286. current = null;
  287. }
  288. /// <summary>
  289. /// 异步下载进度事件(仅对于单个文件)
  290. /// </summary>
  291. void webClient_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
  292. {
  293. DownloadProgressEventArgs args = new DownloadProgressEventArgs((int)((double)e.BytesReceived / (double)fileSize * 100), e.UserState);
  294. args.BytesReceived += e.BytesReceived;
  295. args.FileName = e.UserState.ToString();
  296. args.TotalBytesToReceive = fileSize;
  297. if (current != null)
  298. {
  299. current.Post(onProgressReportDelegate, args);
  300. }
  301. }
  302. /// <summary>
  303. /// 异步下载完成事件(仅对于单个文件)
  304. /// </summary>
  305. /// <param name="sender"></param>
  306. /// <param name="e"></param>
  307. void webClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
  308. {
  309. fileCount++;
  310. if (fileCount == manifest.ManifestFiles.Files.Length)
  311. {
  312. this.CompletionMethod(e.Error, TaskCanceled(current.UserSuppliedState), current);
  313. }
  314. }
  315. /// <summary>
  316. /// 取消异步下载
  317. /// </summary>
  318. public void CancelAsync()
  319. {
  320. CancelAsync(defaultTaskId);
  321. }
  322. /// <summary>
  323. /// 取消异步下载
  324. /// </summary>
  325. public void CancelAsync(object taskId)
  326. {
  327. webClient.CancelAsync();
  328. cancel = true;
  329. current = null;
  330. AsyncOperation asyncOp = userStateToLifetime[taskId] as AsyncOperation;
  331. if (asyncOp != null)
  332. {
  333. lock (userStateToLifetime.SyncRoot)
  334. {
  335. userStateToLifetime.Remove(taskId);
  336. }
  337. }
  338. }
  339. private bool TaskCanceled(object taskId)
  340. {
  341. return cancel || (userStateToLifetime[taskId] == null);
  342. }
  343. private void DoDownloadCompleted(object operationState)
  344. {
  345. DownloadCompleteEventArgs e = operationState as DownloadCompleteEventArgs;
  346. OnDownloadCompleted(e);
  347. }
  348. private void ReportProgress(object state)
  349. {
  350. DownloadProgressEventArgs e = state as DownloadProgressEventArgs;
  351. OnDownloadProgressChanged(e);
  352. }
  353. }
  354. }