C# FileStream实现多线程断点续传

作者:airforce094 时间:2022-06-19 06:50:56 

一、前言

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

代码如下:


using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace TestCenter
{
class Program
{
 static void Main(string[] args)
 {
  string LocalSavePath = @"E:\Test\TestFile\Local\1.msi"; //本地目标文件路径

FileInfo SeverFilePath = new FileInfo(@"E:\Test\TestFile\Server\1.msi"); //服务器待文件路径
  long FileLength = SeverFilePath.Length; //待下载文件大小

Console.WriteLine("Start Configuration");
  int PackCount = 0; //初始化数据包个数

long PackSize = 1024000; //数据包大小

if (FileLength % PackSize > 0)
  {
   PackCount = (int)(FileLength / PackSize) + 1;
  }

else
  {
   PackCount = (int)(FileLength / PackSize);
  }

Console.WriteLine("Start Recieve");
  var tasks = new Task[PackCount]; //多线程任务

for (int index = 0; index < PackCount; index++)
  {

int Threadindex = index; //这步很关键,在Task()里的绝对不能直接使用index
   var task = new Task(() =>
   {
    string tempfilepath = @"E:\Test\TestFile\Temp\" + "QS_" + Threadindex + "_" + PackCount; //临时文件路径

using (FileStream tempstream = new FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write))
    {
     int length = (int)Math.Min(PackSize, FileLength - Threadindex * PackSize);

var bytes = GetFile(Threadindex*PackCount, length);

tempstream.Write(bytes, 0, length);
     tempstream.Flush();
     tempstream.Close();
     tempstream.Dispose();
    }
   });
   tasks[Threadindex] = task;
   task.Start();
  }

Task.WaitAll(tasks); //等待所有线程完成
  Console.WriteLine("Recieve End");

//检测有哪些数据包未下载
  Console.WriteLine("Start Compare");
  DirectoryInfo TempDir = new DirectoryInfo(@"E:\Test\TestFile\temp"); //临时文件夹路径
  List<string> Comparefiles = new List<string>();

for (int i = 0; i < PackCount; i++)
  {
   bool hasfile = false;
   foreach (FileInfo Tempfile in TempDir.GetFiles())
   {
    if (Tempfile.Name.Split('_')[1] == i.ToString())
    {
     hasfile = true;
     break;
    }
   }
   if (hasfile == false)
   {
    Comparefiles.Add(i.ToString());
   }
  }

//最后补上这些缺失的文件
  if (Comparefiles.Count > 0)
  {
   foreach (string com_index in Comparefiles)
   {
    string tempfilepath = @"E:\Test\TestFile\Temp\" + "QS_" + com_index+ "_" + PackCount;
    using (FileStream Compstream = new FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write))
    {
     int length = (int)Math.Min(PackSize, FileLength - Convert.ToInt32(com_index) * PackSize);
     var bytes = GetFile(Convert.ToInt32(com_index)*PackCount, length);
     Compstream.Write(bytes, 0, length);
     Compstream.Flush();
     Compstream.Close();
     Compstream.Dispose();
    }
   }

}
  Console.WriteLine("Compare End");

//准备将临时文件融合并写到1.msi中
  Console.WriteLine("Start Write");
  using (FileStream writestream = new FileStream(LocalSavePath, FileMode.Create, FileAccess.Write, FileShare.Write))
  {
   foreach (FileInfo Tempfile in TempDir.GetFiles())
   {
    using (FileStream readTempStream = new FileStream(Tempfile.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
     long onefileLength = Tempfile.Length;
     byte[] buffer = new byte[Convert.ToInt32(onefileLength)];
     readTempStream.Read(buffer, 0, Convert.ToInt32(onefileLength));
     writestream.Write(buffer, 0, Convert.ToInt32(onefileLength));
    }
   }
   writestream.Flush();
   writestream.Close();
   writestream.Dispose();
  }
  Console.WriteLine("Write End");

//删除临时文件
  Console.WriteLine("Start Delete Temp Files");
  foreach (FileInfo Tempfile in TempDir.GetFiles())
  {
   Tempfile.Delete();
  }
  Console.WriteLine("Delete Success");
  Console.ReadKey();
 }

//这个方法可以放到Remoting或者WCF服务中去,然后本地调用该方法即可实现多线程断点续传
 public static byte[] GetFile(int start, int length)
 {
  string SeverFilePath = @"E:\Test\TestFile\Server\1.msi";
  using (FileStream ServerStream = new FileStream(SeverFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 1024*80, true))
  {
   byte[] buffer = new byte[length];
   ServerStream.Position = start;
   //ServerStream.Seek(start, SeekOrigin.Begin);
   ServerStream.Read(buffer, 0, length);
   return buffer;
  }
 }
}
}

二、讨论     

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

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

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

来源:http://www.cnblogs.com/lovecsharp094/p/5727141.html

标签:C#,FileStream,断点续传
0
投稿

猜你喜欢

  • 在Spring中编写事务的介绍

    2021-12-15 10:33:18
  • 图解Java经典算法冒泡排序的原理与实现

    2023-03-14 21:41:23
  • 浅谈Java中IO和NIO的本质和区别

    2023-11-01 01:44:08
  • IDEA中配置文件模板的添加方法

    2023-10-28 17:52:31
  • 一篇文章弄懂C#中的async和await

    2021-08-15 16:41:14
  • C#图像处理之图像平移的方法

    2021-12-16 08:38:37
  • 关于Java实现HttpServer模拟前端接口调用

    2021-07-10 07:44:17
  • C#精确计算年龄的方法分析

    2021-06-13 14:06:36
  • C# WinForm制作异形窗体与控件的方法

    2023-11-07 11:09:28
  • 深入多线程之:用Wait与Pulse模拟一些同步构造的应用详解

    2021-07-09 06:14:19
  • Java四种权限修饰符知识点详解

    2023-11-11 06:12:59
  • C#实现将程序运行信息写入日志的方法

    2021-11-22 22:30:09
  • Java中Date和Calendar常用方法

    2023-11-12 07:35:32
  • java二维数组基础知识详解

    2023-03-28 11:10:54
  • PC蓝牙通信C#代码实现

    2023-07-06 19:59:32
  • 使用栈的迷宫算法java版代码

    2022-03-07 12:47:16
  • Java多线程之线程通信生产者消费者模式及等待唤醒机制代码详解

    2022-10-31 11:17:22
  • C#中的尾递归与Continuation详解

    2021-12-05 16:35:15
  • 详解Java实践之建造者模式

    2023-01-14 23:03:13
  • jsp+servlet实现简单登录页面功能(附demo)

    2023-09-24 11:32:28
  • asp之家 软件编程 m.aspxhome.com