一、前言
網上有許多的多線程斷點續傳操作,但總是寫的很雲裡霧裡,或者寫的比較坑長。由於這幾個月要負責公司的在線升級項目,所以正好順便寫了一下
代碼如下:
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