PartialDownloader.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. using System;
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Threading;
  8. namespace Masuit.Tools.Net
  9. {
  10. /// <summary>
  11. /// 部分下载器
  12. /// </summary>
  13. public class PartialDownloader
  14. {
  15. #region Variables
  16. /// <summary>
  17. /// 这部分完成事件
  18. /// </summary>
  19. public event EventHandler DownloadPartCompleted;
  20. /// <summary>
  21. /// 部分下载进度改变事件
  22. /// </summary>
  23. public event EventHandler DownloadPartProgressChanged;
  24. /// <summary>
  25. /// 部分下载停止事件
  26. /// </summary>
  27. public event EventHandler DownloadPartStopped;
  28. HttpWebRequest _req;
  29. HttpWebResponse _resp;
  30. Stream _tempStream;
  31. FileStream _file;
  32. private readonly AsyncOperation _aop = AsyncOperationManager.CreateOperation(null);
  33. private readonly Stopwatch _stp;
  34. readonly int[] _lastSpeeds;
  35. int _counter;
  36. bool _stop, _wait;
  37. #endregion
  38. #region PartialDownloader
  39. /// <summary>
  40. /// 部分块下载
  41. /// </summary>
  42. /// <param name="url"></param>
  43. /// <param name="dir"></param>
  44. /// <param name="fileGuid"></param>
  45. /// <param name="from"></param>
  46. /// <param name="to"></param>
  47. /// <param name="rangeAllowed"></param>
  48. public PartialDownloader(string url, string dir, string fileGuid, int from, int to, bool rangeAllowed)
  49. {
  50. _from = from;
  51. _to = to;
  52. _url = url;
  53. _rangeAllowed = rangeAllowed;
  54. _fileGuid = fileGuid;
  55. _directory = dir;
  56. _lastSpeeds = new int[10];
  57. _stp = new Stopwatch();
  58. }
  59. #endregion
  60. void DownloadProcedure()
  61. {
  62. _file = new FileStream(FullPath, FileMode.Create, FileAccess.ReadWrite);
  63. #region Request-Response
  64. _req = WebRequest.Create(_url) as HttpWebRequest;
  65. if (_req != null)
  66. {
  67. _req.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
  68. _req.AllowAutoRedirect = true;
  69. _req.MaximumAutomaticRedirections = 5;
  70. _req.ServicePoint.ConnectionLimit += 1;
  71. _req.ServicePoint.Expect100Continue = true;
  72. _req.ProtocolVersion = HttpVersion.Version10;
  73. ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.SystemDefault;
  74. ServicePointManager.Expect100Continue = true;
  75. if (_rangeAllowed)
  76. _req.AddRange(_from, _to);
  77. _resp = _req.GetResponse() as HttpWebResponse;
  78. #endregion
  79. #region Some Stuff
  80. if (_resp != null)
  81. {
  82. _contentLength = _resp.ContentLength;
  83. if (_contentLength <= 0 || (_rangeAllowed && _contentLength != _to - _from + 1))
  84. throw new Exception("Invalid response content");
  85. _tempStream = _resp.GetResponseStream();
  86. int bytesRead;
  87. byte[] buffer = new byte[4096];
  88. _stp.Start();
  89. #endregion
  90. #region Procedure Loop
  91. while (_tempStream != null && (bytesRead = _tempStream.Read(buffer, 0, buffer.Length)) > 0)
  92. {
  93. while (_wait)
  94. {
  95. }
  96. if (_totalBytesRead + bytesRead > _contentLength)
  97. bytesRead = (int)(_contentLength - _totalBytesRead);
  98. _file.Write(buffer, 0, bytesRead);
  99. _totalBytesRead += bytesRead;
  100. _lastSpeeds[_counter] = (int)(_totalBytesRead / Math.Ceiling(_stp.Elapsed.TotalSeconds));
  101. _counter = (_counter >= 9) ? 0 : _counter + 1;
  102. int tempProgress = (int)(_totalBytesRead * 100 / _contentLength);
  103. if (_progress != tempProgress)
  104. {
  105. _progress = tempProgress;
  106. _aop.Post(state =>
  107. {
  108. DownloadPartProgressChanged?.Invoke(this, EventArgs.Empty);
  109. }, null);
  110. }
  111. if (_stop || (_rangeAllowed && _totalBytesRead == _contentLength))
  112. {
  113. break;
  114. }
  115. }
  116. #endregion
  117. #region Close Resources
  118. _file.Close();
  119. _resp.Close();
  120. }
  121. _tempStream?.Close();
  122. _req.Abort();
  123. }
  124. _stp.Stop();
  125. #endregion
  126. #region Fire Events
  127. if (!_stop && DownloadPartCompleted != null)
  128. _aop.Post(state =>
  129. {
  130. _completed = true;
  131. DownloadPartCompleted(this, EventArgs.Empty);
  132. }, null);
  133. if (_stop && DownloadPartStopped != null)
  134. _aop.Post(state => DownloadPartStopped(this, EventArgs.Empty), null);
  135. #endregion
  136. }
  137. #region Public Methods
  138. /// <summary>
  139. /// 启动下载
  140. /// </summary>
  141. public void Start()
  142. {
  143. _stop = false;
  144. Thread procThread = new Thread(DownloadProcedure);
  145. procThread.Start();
  146. }
  147. /// <summary>
  148. /// 下载停止
  149. /// </summary>
  150. public void Stop()
  151. {
  152. _stop = true;
  153. }
  154. /// <summary>
  155. /// 暂停等待下载
  156. /// </summary>
  157. public void Wait()
  158. {
  159. _wait = true;
  160. }
  161. /// <summary>
  162. /// 稍后唤醒
  163. /// </summary>
  164. public void ResumeAfterWait()
  165. {
  166. _wait = false;
  167. }
  168. #endregion
  169. #region Property Variables
  170. private readonly int _from;
  171. private int _to;
  172. private readonly string _url;
  173. private readonly bool _rangeAllowed;
  174. private long _contentLength;
  175. private int _totalBytesRead;
  176. private readonly string _fileGuid;
  177. private readonly string _directory;
  178. private int _progress;
  179. private bool _completed;
  180. #endregion
  181. #region Properties
  182. /// <summary>
  183. /// 下载已停止
  184. /// </summary>
  185. public bool Stopped => _stop;
  186. /// <summary>
  187. /// 下载已完成
  188. /// </summary>
  189. public bool Completed => _completed;
  190. /// <summary>
  191. /// 下载进度
  192. /// </summary>
  193. public int Progress => _progress;
  194. /// <summary>
  195. /// 下载目录
  196. /// </summary>
  197. public string Directory => _directory;
  198. /// <summary>
  199. /// 文件名
  200. /// </summary>
  201. public string FileName => _fileGuid;
  202. /// <summary>
  203. /// 已读字节数
  204. /// </summary>
  205. public long TotalBytesRead => _totalBytesRead;
  206. /// <summary>
  207. /// 内容长度
  208. /// </summary>
  209. public long ContentLength => _contentLength;
  210. /// <summary>
  211. /// RangeAllowed
  212. /// </summary>
  213. public bool RangeAllowed => _rangeAllowed;
  214. /// <summary>
  215. /// url
  216. /// </summary>
  217. public string Url => _url;
  218. /// <summary>
  219. /// to
  220. /// </summary>
  221. public int To
  222. {
  223. get => _to;
  224. set
  225. {
  226. _to = value;
  227. _contentLength = _to - _from + 1;
  228. }
  229. }
  230. /// <summary>
  231. /// from
  232. /// </summary>
  233. public int From => _from;
  234. /// <summary>
  235. /// 当前位置
  236. /// </summary>
  237. public int CurrentPosition => _from + _totalBytesRead - 1;
  238. /// <summary>
  239. /// 剩余字节数
  240. /// </summary>
  241. public int RemainingBytes => (int)(_contentLength - _totalBytesRead);
  242. /// <summary>
  243. /// 完整路径
  244. /// </summary>
  245. public string FullPath => Path.Combine(_directory, _fileGuid);
  246. /// <summary>
  247. /// 下载速度
  248. /// </summary>
  249. public int SpeedInBytes
  250. {
  251. get
  252. {
  253. if (_completed)
  254. return 0;
  255. int totalSpeeds = _lastSpeeds.Sum();
  256. return totalSpeeds / 10;
  257. }
  258. }
  259. #endregion
  260. }
  261. }