一、前言

网上有许多的多线程断点续传操作,但总是写的很云里雾里,或者写的比较坑长。由于这几个月要负责公司的在线升级项目,所以正好顺便写了一下

代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Threading.Tasks;
  5.  
  6. namespace TestCenter
  7. {
  8. class Program
  9. {
  10. static void Main(string[] args)
  11. {
  12. string LocalSavePath = @"E:\Test\TestFile\Local\1.msi"; //本地目标文件路径
  13.  
  14. FileInfo SeverFilePath = new FileInfo(@"E:\Test\TestFile\Server\1.msi"); //服务器待文件路径
  15. long FileLength = SeverFilePath.Length; //待下载文件大小
  16.  
  17. Console.WriteLine("Start Configuration");
  18. int PackCount = ; //初始化数据包个数
  19.  
  20. long PackSize = ; //数据包大小
  21.  
  22. if (FileLength % PackSize > )
  23. {
  24. PackCount = (int)(FileLength / PackSize) + ;
  25. }
  26.  
  27. else
  28. {
  29. PackCount = (int)(FileLength / PackSize);
  30. }
  31.  
  32. Console.WriteLine("Start Recieve");
  33. var tasks = new Task[PackCount]; //多线程任务
  34.  
  35. for (int index = ; index < PackCount; index++)
  36. {
  37.  
  38. int Threadindex = index; //这步很关键,在Task()里的绝对不能直接使用index
  39. var task = new Task(() =>
  40. {
  41. string tempfilepath = @"E:\Test\TestFile\Temp\" + "QS_" + Threadindex + "_" + PackCount; //临时文件路径
  42.  
  43. using (FileStream tempstream = new FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write))
  44. {
  45. int length = (int)Math.Min(PackSize, FileLength - Threadindex * PackSize);
  46.  
  47. var bytes = GetFile(Threadindex*PackCount, length);
  48.  
  49. tempstream.Write(bytes, , length);
  50. tempstream.Flush();
  51. tempstream.Close();
  52. tempstream.Dispose();
  53. }
  54. });
  55. tasks[Threadindex] = task;
  56. task.Start();
  57. }
  58.  
  59. Task.WaitAll(tasks); //等待所有线程完成
  60. Console.WriteLine("Recieve End");
  61.  
  62. //检测有哪些数据包未下载
  63. Console.WriteLine("Start Compare");
  64. DirectoryInfo TempDir = new DirectoryInfo(@"E:\Test\TestFile\temp"); //临时文件夹路径
  65. List<string> Comparefiles = new List<string>();
  66.  
  67. for (int i = ; i < PackCount; i++)
  68. {
  69. bool hasfile = false;
  70. foreach (FileInfo Tempfile in TempDir.GetFiles())
  71. {
  72. if (Tempfile.Name.Split('_')[] == i.ToString())
  73. {
  74. hasfile = true;
  75. break;
  76. }
  77. }
  78. if (hasfile == false)
  79. {
  80. Comparefiles.Add(i.ToString());
  81. }
  82. }
  83.  
  84. //最后补上这些缺失的文件
  85. if (Comparefiles.Count > )
  86. {
  87. foreach (string com_index in Comparefiles)
  88. {
  89. string tempfilepath = @"E:\Test\TestFile\Temp\" + "QS_" + com_index+ "_" + PackCount;
  90. using (FileStream Compstream = new FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write))
  91. {
  92. int length = (int)Math.Min(PackSize, FileLength - Convert.ToInt32(com_index) * PackSize);
  93. var bytes = GetFile(Convert.ToInt32(com_index)*PackCount, length);
  94. Compstream.Write(bytes, , length);
  95. Compstream.Flush();
  96. Compstream.Close();
  97. Compstream.Dispose();
  98. }
  99. }
  100.  
  101. }
  102. Console.WriteLine("Compare End");
  103.  
  104. //准备将临时文件融合并写到1.msi中
  105. Console.WriteLine("Start Write");
  106. using (FileStream writestream = new FileStream(LocalSavePath, FileMode.Create, FileAccess.Write, FileShare.Write))
  107. {
  108. foreach (FileInfo Tempfile in TempDir.GetFiles())
  109. {
  110. using (FileStream readTempStream = new FileStream(Tempfile.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
  111. {
  112. long onefileLength = Tempfile.Length;
  113. byte[] buffer = new byte[Convert.ToInt32(onefileLength)];
  114. readTempStream.Read(buffer, , Convert.ToInt32(onefileLength));
  115. writestream.Write(buffer, , Convert.ToInt32(onefileLength));
  116. }
  117. }
  118. writestream.Flush();
  119. writestream.Close();
  120. writestream.Dispose();
  121. }
  122. Console.WriteLine("Write End");
  123.  
  124. //删除临时文件
  125. Console.WriteLine("Start Delete Temp Files");
  126. foreach (FileInfo Tempfile in TempDir.GetFiles())
  127. {
  128. Tempfile.Delete();
  129. }
  130. Console.WriteLine("Delete Success");
  131. Console.ReadKey();
  132. }
  133.  
  134. //这个方法可以放到Remoting或者WCF服务中去,然后本地调用该方法即可实现多线程断点续传
  135. public static byte[] GetFile(int start, int length)
  136. {
  137. string SeverFilePath = @"E:\Test\TestFile\Server\1.msi";
  138. using (FileStream ServerStream = new FileStream(SeverFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, *, true))
  139. {
  140. byte[] buffer = new byte[length];
  141. ServerStream.Position = start;
  142. //ServerStream.Seek(start, SeekOrigin.Begin);
  143. ServerStream.Read(buffer, , length);
  144. return buffer;
  145. }
  146. }
  147. }
  148. }

二、讨论

1)需要注意的是第44行,不能直接使用index变量在Task()里进行操作,而是要将它赋给Threadindex,让Threadindex在Task()里,不然会直接报错,为什么呢?

链接:http://bbs.csdn.net/topics/390769774

2)70至108行代码可以在外面再套一层while循环,循环检测临时文件是否下完整了,然后再定义一个检测最大上限,超过这个上限就放弃本次更新,当用户的网络恢复正常后下次再做更新操作。所以说放临时文件的文件夹最好要包含版本信息,不会把2.0.0的临时文件和1.0.0的临时文件搞混。

3) FileStream.Position 与 FileStream.Seek(long offset, SeekOrigin seekorigin) 的作用都是获取流的指针位置,当文件路径使用绝对路径时使用Position;相对路径时使用Seek方法

链接:https://stackoverflow.com/questions/7238929/stream-seek0-seekorigin-begin-or-position-0

C#基础-FileStream实现多线程断点续传的更多相关文章

  1. FileStream实现多线程断点续传(已封装)

    处理文件分片 处理缺失的分片文件 合并分片文件 MD5验证文件 using System; using System.Collections.Generic; using System.IO; usi ...

  2. AsyncTask实现多任务多线程断点续传下载

    这篇博客是AsyncTask下载系列的最后一篇文章,前面写了关于断点续传的和多线程下载的博客,这篇是在前两篇的基础上面实现的,有兴趣的可以去看下. 一.AsyncTask实现断点续传 二.AsyncT ...

  3. Java入门到精通——基础篇之多线程实现简单的PV操作的进程同步

    Java入门到精通——基础篇之多线程实现简单的PV操作的进程同步 一.概述     PV操作是对信号量进行的操作.     进程同步是指在并发进程之间存在一种制约关系,一个进程的执行依赖另一个进程的消 ...

  4. 实现android支持多线程断点续传下载器功能

    多线程断点下载流程图: 多线程断点续传下载原理介绍: 在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度手机端下载数据时难免会出现无信号断线.电量不足等情况,所以需要断点续传功能根据下 ...

  5. android 多线程断点续传下载

    今天跟大家一起分享下Android开发中比较难的一个环节,可能很多人看到这个标题就会感觉头很大,的确如果没有良好的编码能力和逻辑思维,这块是很难搞明白的,前面2次总结中已经为大家分享过有关技术的一些基 ...

  6. Android开发多线程断点续传下载器

    使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线.电量不足等情况下,这就需要使用到断点 ...

  7. Android多线程断点续传下载

    这个月接到一个项目.要写一个像360助手一样的对于软件管理的APP:当中.遇到了一个问题:多线程断点下载 这个 ,因为之前没有写过这方面的应用功能.所以.不免要自学了. 然后就在各个昂站上收索并整理了 ...

  8. Java基础教程:多线程基础(1)——基础操作

    Java:多线程基础(1) 实现多线程的两种方式 1.继承Thread类 public class myThread extends Thread { /** * 继承Thread类,重写RUN方法. ...

  9. Java基础教程:多线程基础(4)——Lock的使用

    Java基础教程:多线程基础(4)——Lock的使用 快速开始 Java 5中Lock对象的也能实现同步的效果,而且在使用上更加方便. 本节重点的2个知识点是:ReentrantLock类的使用和Re ...

随机推荐

  1. 玩转SQL Server复制回路の变更数据类型、未分区表转为分区表

    玩转SQL Server复制回路の变更数据类型.未分区表转为分区表 复制的应用: 初级应用:读写分离.数据库备份 高级应用:搬迁大型数据库(跨机房).变更数据类型.未分区表转为分区表 京东的复制专家 ...

  2. DDD实践问题之 - 关于论坛的帖子回复统计信息的更新的思考

    之前,在用ENode开发forum案例时,遇到了关于如何实现论坛帖子的回复的统计信息如何更新的问题.后来找到了自己认为比较合理的解决方案,分享给大家.也希望能和大家交流,擦出更多的火花. 论坛核心领域 ...

  3. JavaScript多文件下载

    对于文件的下载,可以说是一个十分常见的话题,前端的很多项目中都会有这样的需求,比如 highChart 统计图的导出,在线图片编辑中的图片保存,在线代码编辑的代码导出等等.而很多时候,我们只给了一个链 ...

  4. WCF服务的异常消息

    原创地址:http://www.cnblogs.com/jfzhu/p/4055024.html 转载请注明出处 WCF Service发生异常的时候,客户端一般只能看见这样一个错误:“The ser ...

  5. Pointer's NULL And 0

    问题起源 在使用Qt框架的时候, 经常发现一些构造函数 *parent = 0 这样的代码. 时间长了, 就觉的疑惑了. 一个指针不是等于NULL吗? 这样写, 行得通吗? 自己测试一下就可以了. 测 ...

  6. Spark DAGSheduler生成Stage过程分析实验

    RDD.Action触发SparkContext.run,这里举最简单的例子rdd.count() /** * Return the number of elements in the RDD. */ ...

  7. Vue脚手架工具vue-cli和调试组件vue-devtools

    https://github.com/vuejs/vue-cli npm install vue-cli -g vue init webpack my-project cd my-project // ...

  8. Struts框架

    Struts是最早的Java开源框架之一,它是MVC设计模式的一个优秀实现. Struts定义了通用的Controller(控制器),通过配置文件(通常是 Struts -config.xml) Ec ...

  9. Android开发-之环境的搭建

    关于Android开发可以使用的工具有eclipse和Android studio等,这两个工具都各有各的好处和不足.studio是谷歌推出的一款开发工具,而我们都知道Android就是谷歌公司的,所 ...

  10. 【Win10 应用开发】人脸识别

    可能你会认为人脸识别用起来会很复杂,老周当初也这么想,但通过实际操作后,我发现非然. 经过微软封装的东西,向来都是复杂问题简单化,只要用得舒心,代码越少越好,用最少的代码做最多的事情,此为大师境界也. ...