溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C# FileStream實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳

發(fā)布時(shí)間:2020-09-19 13:40:23 來源:腳本之家 閱讀:186 作者:airforce094 欄目:編程語言

一、前言

       網(wǎng)上有許多的多線程斷點(diǎn)續(xù)傳操作,但總是寫的很云里霧里,或者寫的比較坑長(zhǎng)。由于這幾個(gè)月要負(fù)責(zé)公司的在線升級(jí)項(xiàng)目,所以正好順便寫了一下

代碼如下:

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"; //本地目標(biāo)文件路徑

   FileInfo SeverFilePath = new FileInfo(@"E:\Test\TestFile\Server\1.msi"); //服務(wù)器待文件路徑
   long FileLength = SeverFilePath.Length; //待下載文件大小


   Console.WriteLine("Start Configuration");
   int PackCount = 0; //初始化數(shù)據(jù)包個(gè)數(shù)

   long PackSize = 1024000; //數(shù)據(jù)包大小

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

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


   Console.WriteLine("Start Recieve");
   var tasks = new Task[PackCount]; //多線程任務(wù)

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


    int Threadindex = index; //這步很關(guān)鍵,在Task()里的絕對(duì)不能直接使用index
    var task = new Task(() =>
    {
     string tempfilepath = @"E:\Test\TestFile\Temp\" + "QS_" + Threadindex + "_" + PackCount; //臨時(shí)文件路徑

     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");


   //檢測(cè)有哪些數(shù)據(jù)包未下載
   Console.WriteLine("Start Compare");
   DirectoryInfo TempDir = new DirectoryInfo(@"E:\Test\TestFile\temp"); //臨時(shí)文件夾路徑
   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());
    }
   }

   //最后補(bǔ)上這些缺失的文件
   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");


   //準(zhǔn)備將臨時(shí)文件融合并寫到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");



   //刪除臨時(shí)文件
   Console.WriteLine("Start Delete Temp Files");
   foreach (FileInfo Tempfile in TempDir.GetFiles())
   {
    Tempfile.Delete();
   }
   Console.WriteLine("Delete Success");
   Console.ReadKey();
  }


  //這個(gè)方法可以放到Remoting或者WCF服務(wù)中去,然后本地調(diào)用該方法即可實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳
  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()里進(jìn)行操作,而是要將它賦給Threadindex,讓Threadindex在Task()里,不然會(huì)直接報(bào)錯(cuò),為什么呢?查看鏈接

2)70至108行代碼可以在外面再套一層while循環(huán),循環(huán)檢測(cè)臨時(shí)文件是否下完整了,然后再定義一個(gè)檢測(cè)最大上限,超過這個(gè)上限就放棄本次更新,當(dāng)用戶的網(wǎng)絡(luò)恢復(fù)正常后下次再做更新操作。所以說放臨時(shí)文件的文件夾最好要包含版本信息,不會(huì)把2.0.0的臨時(shí)文件和1.0.0的臨時(shí)文件搞混。

3) FileStream.Position 與 FileStream.Seek(long offset, SeekOrigin seekorigin) 的作用都是獲取流的指針位置,當(dāng)文件路徑使用絕對(duì)路徑時(shí)使用Position;相對(duì)路徑時(shí)使用Seek方法,查看鏈接

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI