一、前言
網上有許多的多線程斷點續傳操作,但總是寫的很雲裡霧裡,或者寫的比較坑長。由於這幾個月要負責公司的在線升級項目,所以正好順便寫了一下
代碼如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.IO; 6 using System.Threading; 7 using System.Threading.Tasks; 8 9 namespace ConsoleStream 10 { 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 string LocalSavePath = @"E:\Test\Test\local\1.msi"; //本地目標文件路徑 16 17 System.IO.FileInfo SeverFilePath = new FileInfo(@"E:\Test\Test\server\1.msi"); //服務器待文件路徑 18 long FileLength = SeverFilePath.Length; //待下載文件大小 19 20 21 Console.WriteLine("Start Configuration"); 22 int PackCount = 0; //初始化數據包個數 23 24 long PackSize = 10240000; //數據包大小 25 26 if (FileLength % PackSize> 0) 27 { 28 PackCount = (int)(FileLength / PackSize) + 1; 29 } 30 31 else 32 { 33 PackCount = (int)(FileLength / PackSize); 34 } 35 36 37 Console.WriteLine("Start Recieve"); 38 var tasks = new Task[PackCount]; //多線程任務 39 40 for (int index = 0; index < PackCount; index++) 41 { 42 43 44 int Threadindex = index; //這步很關鍵,在Task()裡的絕對不能直接使用index 45 var task = new Task(() => 46 { 47 string tempfilepath = @"E:\Test\Test\temp\" + "QS_" + Threadindex.ToString() + "_" + PackCount.ToString(); //臨時文件路徑 48 49 using (System.IO.FileStream tempstream = new System.IO.FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write)) 50 { 51 int length = (int)Math.Min(PackSize, FileLength - Threadindex * PackSize); 52 53 var bytes = GetFile(Threadindex, length); 54 55 tempstream.Write(bytes, 0, length); 56 tempstream.Flush(); 57 tempstream.Close(); 58 tempstream.Dispose(); 59 } 60 }); 61 tasks[Threadindex] = task; 62 task.Start(); 63 } 64 65 Task.WaitAll(tasks); //等待所有線程完成 66 Console.WriteLine("Recieve End"); 67 68 69 //檢測有哪些數據包未下載 70 Console.WriteLine("Start Compare"); 71 DirectoryInfo TempDir = new DirectoryInfo(@"E:\Test\Test\temp"); //臨時文件夾路徑 72 List<string> Comparefiles = new List<string>(); 73 bool hasfile = false; 74 for (int i = 0; i < PackCount; i++) 75 { 76 foreach (FileInfo Tempfile in TempDir.GetFiles()) 77 { 78 if (Tempfile.Name.Split('_')[1] == i.ToString()) 79 { 80 hasfile = true; 81 break; 82 } 83 } 84 if (hasfile == false) 85 { 86 Comparefiles.Add(i.ToString()); 87 } 88 } 89 90 //最後補上這些缺失的文件 91 if (Comparefiles.Count > 0) 92 { 93 foreach (string com_index in Comparefiles) 94 { 95 string tempfilepath = @"E:\Test\Test\temp\" + "QS_" + com_index.ToString() + "_" + PackCount.ToString(); 96 using (System.IO.FileStream Compstream = new System.IO.FileStream(tempfilepath, FileMode.Create, FileAccess.Write, FileShare.Write)) 97 { 98 int length = (int)Math.Min(PackSize, FileLength - Convert.ToInt32(com_index) * PackSize); 99 var bytes = GetFile(Convert.ToInt32(com_index), length); 100 Compstream.Write(bytes, 0, length); 101 Compstream.Flush(); 102 Compstream.Close(); 103 Compstream.Dispose(); 104 } 105 } 106 107 } 108 Console.WriteLine("Compare End"); 109 110 111 //准備將臨時文件融合並寫到1.msi中 112 Console.WriteLine("Start Write"); 113 using (System.IO.FileStream writestream = new System.IO.FileStream(LocalSavePath, FileMode.Create, FileAccess.Write, FileShare.Write)) 114 { 115 foreach (FileInfo Tempfile in TempDir.GetFiles()) 116 { 117 using (System.IO.FileStream readTempStream = new System.IO.FileStream(Tempfile.FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read, FileShare.ReadWrite)) 118 { 119 long onefileLength = Tempfile.Length; 120 byte[] buffer = new byte[Convert.ToInt32(onefileLength)]; 121 readTempStream.Read(buffer, 0, Convert.ToInt32(onefileLength)); 122 writestream.Write(buffer, 0, Convert.ToInt32(onefileLength)); 123 } 124 } 125 writestream.Flush(); 126 writestream.Close(); 127 writestream.Dispose(); 128 } 129 Console.WriteLine("Write End"); 130 131 132 133 //刪除臨時文件 134 Console.WriteLine("Start Delete Temp Files"); 135 foreach (FileInfo Tempfile in TempDir.GetFiles()) 136 { 137 Tempfile.Delete(); 138 } 139 Console.WriteLine("Delete Success"); 140 Console.ReadKey(); 141 } 142 143 144 //這個方法可以放到Remoting或者WCF服務中去,然後本地調用該方法即可實現多線程斷點續傳 145 public static byte[] GetFile(int start, int length) 146 { 147 string SeverFilePath = @"E:\Test\Test\server\1.msi"; 148 using (System.IO.FileStream ServerStream = new System.IO.FileStream(SeverFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, FileShare.ReadWrite,1024,true)) 149 { 150 byte[] buffer = new byte[length]; 151 ServerStream.Position = start; 152 ServerStream.Read(buffer, 0, length); 153 return buffer; 154 } 155 } 156 157 158 } 159 }
二、討論
需要注意的是第44行,不能直接使用index變量在Task()裡進行操作,而是要將它賦給Threadindex,讓Threadindex在Task()裡,不然會直接報錯,為什麼呢?
答案在此:http://bbs.csdn.net/topics/390769774