• C#基础-FileStream实现多线程断点续传


    一、前言

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

           代码如下:

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

    二、讨论      

    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

  • 相关阅读:
    scala之 spark连接SQL和HIVE/IDEA操作HDFS
    hive之 连接DBeaver
    hive之 配置的图解
    Hive和sparksql中的dayofweek
    spark SQL之 DataFrame使用
    spark SQL之 org.apache.spark.sql.AnalysisException: Table or view not found:
    Spring详解(七)------AOP 注解
    Spring详解(六)------AspectJ 实现AOP
    Spring详解(五)------面向切面编程
    Spring详解(十)------spring 环境切换
  • 原文地址:https://www.cnblogs.com/lovecsharp094/p/5727141.html
Copyright © 2020-2023  润新知