星火微课系统客户端
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

DownloadManager.cs 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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 = 0;//开始下载的位置
  17. private bool isRun = false;//是否正在进行
  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. (
  101. dlInfo.downloadUrlList[0],
  102. 0,
  103. 0,
  104. dlInfo.method
  105. ))
  106. {
  107. //获取文件名,如果包含附件名称则取下附件,否则从url获取名称
  108. string Disposition = rsp.Headers["Content-Disposition"];
  109. try
  110. {
  111. if (Disposition != null)
  112. {
  113. dlInfo.fileName = Disposition.Split('=')[1];
  114. }
  115. else
  116. {
  117. dlInfo.fileName = Path.GetFileName(rsp.ResponseUri.AbsolutePath);
  118. }
  119. }
  120. catch (Exception) //无法获取文件名时 使用传入保存名称
  121. {
  122. dlInfo.fileName = dlInfo.saveFileName;
  123. }
  124. //默认给流总数
  125. dlInfo.count = rsp.ContentLength;
  126. //尝试获取 Content-Range 头部,不为空说明支持断点续传
  127. string contentRange = rsp.Headers["Content-Range"];
  128. if (contentRange != null)
  129. {
  130. //支持断点续传的话,就取range 这里的总数
  131. dlInfo.count = long.Parse(rsp.Headers["Content-Range"].Split('/')[1]);
  132. dlInfo.IsSupportMultiThreading = true;
  133. //生成一个临时文件名
  134. //var tempFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(dlInfo.fileName)).ToUpper();
  135. //tempFileName = tempFileName.Length > 32 ? tempFileName.Substring(0, 32) : tempFileName;
  136. //dlInfo.tempFileName = "test"; //tempFileName + DateTime.Now.ToString("yyyyMMddHHmmssfff");
  137. ///创建线程信息
  138. ///
  139. GetTaskInfo(dlInfo);
  140. }
  141. else
  142. {
  143. //不支持断点续传则一开始就直接读完整流
  144. Save(GetRealFileName(dlInfo), rsp.GetResponseStream());
  145. OnFineshHandler();
  146. }
  147. }
  148. }
  149. catch (Exception ex)
  150. {
  151. string ErrMessage = "【下载】(DownloadManager):" + ex.Message;
  152. LogHelper.WriteErrLog(ErrMessage, ex);
  153. OnDisconnectHandler();
  154. return;
  155. }
  156. dlInfo.isNewTask = false;
  157. }
  158. //如果支持断点续传采用这个
  159. if (dlInfo.IsSupportMultiThreading)
  160. {
  161. StartTask(dlInfo);
  162. //等待合并
  163. while (dls.Where(td => !td.isFinish).Count() > 0 && isRun)
  164. {
  165. Thread.Sleep(100);
  166. }
  167. if ((dls.Where(td => !td.isFinish).Count() == 0))
  168. {
  169. CombineFiles(dlInfo);
  170. OnFineshHandler();
  171. }
  172. }
  173. }).BeginInvoke(null, null);
  174. }
  175. /// <summary>
  176. /// 合成文件
  177. /// </summary>
  178. /// <param name="dlInfo"></param>
  179. private void CombineFiles(DownloadInfoModel dlInfo)
  180. {
  181. string realFilePath = GetRealFileName(dlInfo);
  182. //合并数据
  183. byte[] buffer = new byte[2048];
  184. int length;
  185. using (FileStream fileStream = File.Open(realFilePath, FileMode.CreateNew))
  186. {
  187. for (int i = 0; i < dlInfo.TaskInfoList.Count; i++)
  188. {
  189. string tempFile = dlInfo.TaskInfoList[i].filePath;
  190. using (FileStream tempStream = File.Open(tempFile, FileMode.Open))
  191. {
  192. while ((length = tempStream.Read(buffer, 0, buffer.Length)) > 0)
  193. {
  194. fileStream.Write(buffer, 0, length);
  195. }
  196. tempStream.Flush();
  197. }
  198. //File.Delete(tempFile);
  199. }
  200. }
  201. }
  202. /// <summary>
  203. /// 创建文件
  204. /// </summary>
  205. /// <param name="dlInfo"></param>
  206. /// <returns></returns>
  207. private static string GetRealFileName(DownloadInfoModel dlInfo)
  208. {
  209. //创建正式文件名,如果已存在则加数字序号创建,避免覆盖
  210. int fileIndex = 0;
  211. string realFilePath = Path.Combine(dlInfo.saveDir, dlInfo.saveFileName);
  212. while (File.Exists(realFilePath))
  213. {
  214. realFilePath = Path.Combine(dlInfo.saveDir, string.Format("{0}_{1}", fileIndex++, dlInfo.fileName));
  215. }
  216. return realFilePath;
  217. }
  218. private void StartTask(DownloadInfoModel dlInfo)
  219. {
  220. dls = new List<DownloadService>();
  221. if (dlInfo.TaskInfoList != null)
  222. {
  223. foreach (TaskInfoModel item in dlInfo.TaskInfoList)
  224. {
  225. DownloadService dl = new DownloadService();
  226. dl.OnDownload += OnDownloadHandler;
  227. dl.OnDisconnect += OnDisconnectHandler;
  228. dls.Add(dl);
  229. try
  230. {
  231. dl.Start(item, dlInfo.isReStart);
  232. }
  233. catch (Exception ex)
  234. {
  235. string ErrMessage = "【下载】(DownloadManager):" + ex.Message;
  236. LogHelper.WriteErrLog(ErrMessage, ex);
  237. }
  238. }
  239. }
  240. }
  241. /// <summary>
  242. /// 创建下载任务
  243. /// </summary>
  244. /// <param name="dlInfo"></param>
  245. private void GetTaskInfo(DownloadInfoModel dlInfo)
  246. {
  247. long pieceSize = (dlInfo.count) / dlInfo.taskCount;
  248. dlInfo.TaskInfoList = new List<TaskInfoModel>();
  249. //Random rand = new Random();
  250. int urlIndex = 0;
  251. for (int i = 0; i <= dlInfo.taskCount + 1; i++)
  252. {
  253. long from = (i * pieceSize);
  254. if (from >= dlInfo.count)
  255. {
  256. break;
  257. }
  258. long to = from + pieceSize;
  259. if (to >= dlInfo.count)
  260. {
  261. to = dlInfo.count;
  262. }
  263. dlInfo.TaskInfoList.Add(
  264. new TaskInfoModel
  265. {
  266. method = dlInfo.method,
  267. downloadUrl = dlInfo.downloadUrlList[urlIndex++],
  268. filePath = Path.Combine(dlInfo.saveDir, dlInfo.tempFileName + ".temp" + i),
  269. fromIndex = from,
  270. toIndex = to
  271. });
  272. if (urlIndex >= dlInfo.downloadUrlList.Count)
  273. {
  274. urlIndex = 0;
  275. }
  276. }
  277. }
  278. /// <summary>
  279. /// 保存内容
  280. /// </summary>
  281. /// <param name="filePath"></param>
  282. /// <param name="stream"></param>
  283. private void Save(string filePath, Stream stream)
  284. {
  285. try
  286. {
  287. using (FileStream writer = File.Open(filePath, FileMode.Append))
  288. {
  289. using (stream)
  290. {
  291. int repeatTimes = 0;
  292. byte[] buffer = new byte[1024];
  293. int length = 0;
  294. while ((length = stream.Read(buffer, 0, buffer.Length)) > 0 && isRun)
  295. {
  296. writer.Write(buffer, 0, length);
  297. fromIndex += length;
  298. if (repeatTimes % 5 == 0)
  299. {
  300. writer.Flush();//一定大小就刷一次缓冲区
  301. OnDownloadHandler();
  302. }
  303. repeatTimes++;
  304. }
  305. writer.Flush();
  306. OnDownloadHandler();
  307. }
  308. }
  309. }
  310. catch (Exception)
  311. {
  312. //异常也不影响
  313. }
  314. }
  315. /// <summary>
  316. /// 开始下载
  317. /// </summary>
  318. private void OnStartHandler()
  319. {
  320. new Action(() =>
  321. {
  322. OnStart.Invoke();
  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. }