星火微课系统客户端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DownloadManager.cs 14KB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. using Common.Model;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Threading;
  7. namespace Common.system
  8. {
  9. /// <summary>
  10. /// 下载方法
  11. /// 创建人:赵耀
  12. /// 创建时间:2018年10月30日
  13. /// </summary>
  14. public class DownloadManager
  15. {
  16. private long _fromIndex; //开始下载的位置
  17. private bool _isRun; //是否正在进行
  18. private DownloadInfoModel _dlInfo;
  19. private List<DownloadService> _dls = new List<DownloadService>();
  20. /// <summary>
  21. /// 开始下载
  22. /// </summary>
  23. public event Action OnStart;
  24. /// <summary>
  25. /// 停止下载
  26. /// </summary>
  27. public event Action OnStop;
  28. /// <summary>
  29. /// 下载进度
  30. /// </summary>
  31. public event Action<long, long, string> OnDownload;
  32. /// <summary>
  33. /// 下载完成
  34. /// </summary>
  35. public event Action<string> OnFinsh;
  36. /// <summary>
  37. /// 断开网络连接
  38. /// </summary>
  39. public event Action<string> OnDisconnect;
  40. public DownloadManager(DownloadInfoModel dlInfo)
  41. {
  42. this._dlInfo = dlInfo;
  43. }
  44. /// <summary>
  45. /// 暂停
  46. /// </summary>
  47. public void Stop()
  48. {
  49. _isRun = false;
  50. _dls.ForEach(dl => dl.Stop());
  51. OnStopHandler();
  52. }
  53. /// <summary>
  54. /// 开始下载
  55. /// </summary>
  56. public void Start()
  57. {
  58. _dlInfo.isReStart = false;
  59. WorkStart();
  60. }
  61. /// <summary>
  62. /// 重新下载
  63. /// </summary>
  64. public void ReStart()
  65. {
  66. _dlInfo.isReStart = true;
  67. WorkStart();
  68. }
  69. /// <summary>
  70. /// 下载
  71. /// </summary>
  72. private void WorkStart()
  73. {
  74. new Action(() =>
  75. {
  76. if (_dlInfo.isReStart)
  77. {
  78. Stop();
  79. }
  80. while (_dls.Where(dl => !dl.isStopped).Count() > 0)
  81. {
  82. if (_dlInfo.isReStart)
  83. {
  84. Thread.Sleep(100);
  85. }
  86. else
  87. {
  88. return;
  89. }
  90. }
  91. _isRun = true;
  92. OnStartHandler();
  93. //首次任务或者不支持断点续传的进入
  94. if (_dlInfo.isNewTask || (!_dlInfo.isNewTask && !_dlInfo.IsSupportMultiThreading))
  95. {
  96. try
  97. {
  98. //第一次请求获取一小块数据,根据返回的情况判断是否支持断点续传
  99. using (System.Net.WebResponse rsp = ZHttpUtil.Download(
  100. _dlInfo.downloadUrlList[0],
  101. 0,
  102. 0,
  103. _dlInfo.method
  104. ))
  105. {
  106. //获取文件名,如果包含附件名称则取下附件,否则从url获取名称
  107. string disposition = rsp.Headers["Content-Disposition"];
  108. try
  109. {
  110. if (disposition != null)
  111. {
  112. _dlInfo.fileName = disposition.Split('=')[1];
  113. }
  114. else
  115. {
  116. _dlInfo.fileName = Path.GetFileName(rsp.ResponseUri.AbsolutePath);
  117. }
  118. }
  119. catch (Exception) //无法获取文件名时 使用传入保存名称
  120. {
  121. _dlInfo.fileName = _dlInfo.saveFileName;
  122. }
  123. //默认给流总数
  124. _dlInfo.count = rsp.ContentLength;
  125. //尝试获取 Content-Range 头部,不为空说明支持断点续传
  126. string contentRange = rsp.Headers["Content-Range"];
  127. if (contentRange != null)
  128. {
  129. //支持断点续传的话,就取range 这里的总数
  130. _dlInfo.count = long.Parse(rsp.Headers["Content-Range"].Split('/')[1]);
  131. _dlInfo.IsSupportMultiThreading = true;
  132. //生成一个临时文件名
  133. //var tempFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(dlInfo.fileName)).ToUpper();
  134. //tempFileName = tempFileName.Length > 32 ? tempFileName.Substring(0, 32) : tempFileName;
  135. //dlInfo.tempFileName = "test"; //tempFileName + DateTime.Now.ToString("yyyyMMddHHmmssfff");
  136. GetTaskInfo(_dlInfo);
  137. }
  138. else
  139. {
  140. //不支持断点续传则一开始就直接读完整流
  141. Save(GetRealFileName(_dlInfo), rsp.GetResponseStream());
  142. OnFineshHandler();
  143. }
  144. }
  145. }
  146. catch (Exception ex)
  147. {
  148. string errMessage = "【下载】(DownloadManager):" + ex.Message;
  149. LogHelper.Logerror.Error(errMessage, ex);
  150. OnDisconnectHandler();
  151. return;
  152. }
  153. _dlInfo.isNewTask = false;
  154. }
  155. //如果支持断点续传采用这个
  156. if (_dlInfo.IsSupportMultiThreading)
  157. {
  158. StartTask(_dlInfo);
  159. //等待合并
  160. while (_dls.Count(td => !td.isFinish) > 0 && _isRun)
  161. {
  162. Thread.Sleep(100);
  163. }
  164. if ((_dls.Count(td => !td.isFinish) == 0))
  165. {
  166. CombineFiles(_dlInfo);
  167. OnFineshHandler();
  168. }
  169. }
  170. }).BeginInvoke(null, null);
  171. }
  172. /// <summary>
  173. /// 合成文件
  174. /// </summary>
  175. /// <param name="dlInfo"></param>
  176. private void CombineFiles(DownloadInfoModel dlInfo)
  177. {
  178. string realFilePath = GetRealFileName(dlInfo);
  179. //合并数据
  180. byte[] buffer = new byte[2048];
  181. int length;
  182. using (FileStream fileStream = File.Open(realFilePath, FileMode.CreateNew))
  183. {
  184. for (int i = 0; i < dlInfo.TaskInfoList.Count; i++)
  185. {
  186. string tempFile = dlInfo.TaskInfoList[i].filePath;
  187. using (FileStream tempStream = File.Open(tempFile, FileMode.Open))
  188. {
  189. while ((length = tempStream.Read(buffer, 0, buffer.Length)) > 0)
  190. {
  191. fileStream.Write(buffer, 0, length);
  192. }
  193. tempStream.Flush();
  194. }
  195. //File.Delete(tempFile);
  196. }
  197. }
  198. }
  199. /// <summary>
  200. /// 创建文件
  201. /// </summary>
  202. /// <param name="dlInfo"></param>
  203. /// <returns></returns>
  204. private static string GetRealFileName(DownloadInfoModel dlInfo)
  205. {
  206. //创建正式文件名,如果已存在则加数字序号创建,避免覆盖
  207. int fileIndex = 0;
  208. string realFilePath = Path.Combine(dlInfo.saveDir, dlInfo.saveFileName);
  209. while (File.Exists(realFilePath))
  210. {
  211. realFilePath = Path.Combine(dlInfo.saveDir, string.Format("{0}_{1}", fileIndex++, dlInfo.fileName));
  212. }
  213. return realFilePath;
  214. }
  215. private void StartTask(DownloadInfoModel dlInfo)
  216. {
  217. _dls = new List<DownloadService>();
  218. if (dlInfo.TaskInfoList != null)
  219. {
  220. foreach (TaskInfoModel item in dlInfo.TaskInfoList)
  221. {
  222. DownloadService dl = new DownloadService();
  223. dl.OnDownload += OnDownloadHandler;
  224. dl.OnDisconnect += OnDisconnectHandler;
  225. _dls.Add(dl);
  226. try
  227. {
  228. dl.Start(item, dlInfo.isReStart);
  229. }
  230. catch (Exception ex)
  231. {
  232. string errMessage = "【下载】(DownloadManager):" + ex.Message;
  233. LogHelper.Logerror.Error(errMessage, ex);
  234. }
  235. }
  236. }
  237. }
  238. /// <summary>
  239. /// 创建下载任务
  240. /// </summary>
  241. /// <param name="dlInfo"></param>
  242. private void GetTaskInfo(DownloadInfoModel dlInfo)
  243. {
  244. long pieceSize = (dlInfo.count) / dlInfo.taskCount;
  245. dlInfo.TaskInfoList = new List<TaskInfoModel>();
  246. //Random rand = new Random();
  247. int urlIndex = 0;
  248. for (int i = 0; i <= dlInfo.taskCount + 1; i++)
  249. {
  250. long from = (i * pieceSize);
  251. if (from >= dlInfo.count)
  252. {
  253. break;
  254. }
  255. long to = from + pieceSize;
  256. if (to >= dlInfo.count)
  257. {
  258. to = dlInfo.count;
  259. }
  260. dlInfo.TaskInfoList.Add(
  261. new TaskInfoModel
  262. {
  263. method = dlInfo.method,
  264. downloadUrl = dlInfo.downloadUrlList[urlIndex++],
  265. filePath = Path.Combine(dlInfo.saveDir, dlInfo.tempFileName + ".temp" + i),
  266. fromIndex = from,
  267. toIndex = to
  268. });
  269. if (urlIndex >= dlInfo.downloadUrlList.Count)
  270. {
  271. urlIndex = 0;
  272. }
  273. }
  274. }
  275. /// <summary>
  276. /// 保存内容
  277. /// </summary>
  278. /// <param name="filePath"></param>
  279. /// <param name="stream"></param>
  280. private void Save(string filePath, Stream stream)
  281. {
  282. try
  283. {
  284. using (FileStream writer = File.Open(filePath, FileMode.Append))
  285. {
  286. using (stream)
  287. {
  288. int repeatTimes = 0;
  289. byte[] buffer = new byte[1024];
  290. int length;
  291. while ((length = stream.Read(buffer, 0, buffer.Length)) > 0 && _isRun)
  292. {
  293. writer.Write(buffer, 0, length);
  294. _fromIndex += length;
  295. if (repeatTimes % 5 == 0)
  296. {
  297. writer.Flush(); //一定大小就刷一次缓冲区
  298. OnDownloadHandler();
  299. }
  300. repeatTimes++;
  301. }
  302. writer.Flush();
  303. OnDownloadHandler();
  304. }
  305. }
  306. }
  307. catch (Exception)
  308. {
  309. //异常也不影响
  310. }
  311. }
  312. /// <summary>
  313. /// 开始下载
  314. /// </summary>
  315. private void OnStartHandler()
  316. {
  317. new Action(() =>
  318. {
  319. if (OnStart != null)
  320. {
  321. OnStart.Invoke();
  322. }
  323. }).BeginInvoke(null, null);
  324. }
  325. /// <summary>
  326. /// 暂停下载
  327. /// </summary>
  328. private void OnStopHandler()
  329. {
  330. new Action(() =>
  331. {
  332. OnStop?.Invoke();
  333. }).BeginInvoke(null, null);
  334. }
  335. /// <summary>
  336. /// 下载完成
  337. /// </summary>
  338. private void OnFineshHandler()
  339. {
  340. new Action(() =>
  341. {
  342. for (int i = 0; i < _dlInfo.TaskInfoList.Count; i++)
  343. {
  344. string tempFile = _dlInfo.TaskInfoList[i].filePath;
  345. File.Delete(tempFile);
  346. }
  347. OnFinsh?.Invoke(_dlInfo.saveFileName);
  348. }).BeginInvoke(null, null);
  349. }
  350. /// <summary>
  351. /// 下载进度
  352. /// </summary>
  353. private void OnDownloadHandler()
  354. {
  355. new Action(() =>
  356. {
  357. long current = GetDownloadLength();
  358. OnDownload?.Invoke(current, _dlInfo.count, _dlInfo.saveFileName);
  359. }).BeginInvoke(null, null);
  360. }
  361. /// <summary>
  362. /// 断开网络连接
  363. /// </summary>
  364. private void OnDisconnectHandler()
  365. {
  366. new Action(() =>
  367. {
  368. OnDisconnect?.Invoke(_dlInfo.saveFileName);
  369. }).BeginInvoke(null, null);
  370. }
  371. /// <summary>
  372. /// 当前下载进度
  373. /// </summary>
  374. /// <returns></returns>
  375. public long GetDownloadLength()
  376. {
  377. if (_dlInfo.IsSupportMultiThreading)
  378. {
  379. return _dls.Sum(dl => dl.GetDownloadedCount());
  380. }
  381. else
  382. {
  383. return _fromIndex;
  384. }
  385. }
  386. /// <summary>
  387. /// 获取保存文件名
  388. /// </summary>
  389. /// <returns></returns>
  390. public DownloadInfoModel GetFileInfo => _dlInfo;
  391. }
  392. }