软件编程
位置:首页>> 软件编程>> C#编程>> C# FileStream实现多线程断点续传

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

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

标签:C#,FileStream,断点续传

一、前言

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

代码如下:


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

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com