在上一篇中,主要介紹了客戶端的斷點續傳的處理,這一篇,主要補充下服務端的斷點續傳。
服務端單線程斷點續傳
1、獲取上次傳輸的斷點
var filePath = Path.Combine(rootFolderPath, document.WellId.ToString(), query.FileId + ".temp");
if (System.IO.File.Exists(filePath))
{
var length = new FileInfo(filePath).Length;
if (query.TotalSize > length)
{
response.Results = length;
}
}
2、單線程寫入
1 private WebApiResponse WriteToFile(string rootFilePath, Guid wellId, string fileId, string ext, IHttpFile file, bool isNotChunk, bool isLastChunk)
2 {
3 WebApiResponse response = new WebApiResponse();
4
5 try
6 {
7 var folderPath = Path.Combine(rootFilePath, wellId.ToString());
8
9 if (Directory.Exists(folderPath) == false)
10 {
11 Directory.CreateDirectory(folderPath);
12 }
13 if (isNotChunk)
14 {
15 var filePath = Path.Combine(folderPath, fileId + ext);
16
17 if (System.IO.File.Exists(filePath))
18 {
19 System.IO.File.Delete(filePath);
20 }
21
22 using var stream = new FileStream(filePath, FileMode.CreateNew);
23 file.InputStream.WriteTo(stream);
24
25 }
26 else
27 {
28 //附加到臨時文件
29 var filePath = Path.Combine(folderPath, fileId + ".temp");
30 using var stream = new FileStream(filePath, FileMode.Append);
31 file.InputStream.WriteTo(stream);
32 stream?.Close();
33
34 if (isLastChunk)
35 {
36 //最後一個分片,更新文件名
37
38 var tempFilePath = Path.Combine(folderPath, fileId + ".temp");
39
40 var targetFilePath = Path.Combine(folderPath, fileId + ext);
41
42 System.IO.File.Move(tempFilePath, targetFilePath, true);
43 }
44 }
45 }
46 catch (Exception ex)
47 {
48 return WebApiResponse.Fail("文件上傳中出錯:" + ex.Message);
49 }
50
51 return response;
52 }
服務端多線程斷點續傳
1、獲取上次已經上傳的分片
1 //獲取臨時文件夾中文件數,減去1,防止分片不完整
2 var mergeDirPath = Path.Combine(rootFolderPath, document.WellId.ToString(), "Temp");
3
4 if (Directory.Exists(mergeDirPath))
5 {
6 DirectoryInfo di = new DirectoryInfo(mergeDirPath);
7
8 //刪除掉一些fileId對應不上的文件
9
10 var tempfiles = di.GetFiles();
11
12 for (int i = 0; i < tempfiles.Length; i++)
13 {
14 if (!tempfiles[i].Name.Contains(query.FileId))
15 {
16 tempfiles[i].Delete();
17 }
18 }
19
20 var files = di.GetFiles().OrderBy(f => f.CreationTime).ToList();
21
22 List<int> seqs = new List<int>();
23
24 foreach (var item in files)
25 {
26 //去掉小於片區的文件
27 if (item.Length != query.ChunkSize) continue;
28 var pName = Path.GetFileNameWithoutExtension(item.Name);
29
30 var s = pName.Last().ToString();
31
32 seqs.Add(int.Parse(s));
33 }
34
35 response.Results = seqs;
36 }
2、多線程寫入
1 private WebApiResponse MulThreadWriteToFile(string rootFilePath, Guid wellId, string fileId, IHttpFile file, long chunkNumber)
2 {
3 WebApiResponse response = new WebApiResponse();
4
5 try
6 {
7 var folderPath = Path.Combine(rootFilePath, wellId.ToString(), "Temp");
8
9 if (Directory.Exists(folderPath) == false)
10 {
11 Directory.CreateDirectory(folderPath);
12 }
13
14 var filePath = Path.Combine(folderPath, fileId + chunkNumber + ".temp");
15
16 if (System.IO.File.Exists(filePath))
17 {
18 System.IO.File.Delete(filePath);
19 }
20
21 using var stream = new FileStream(filePath, FileMode.CreateNew);
22 file.InputStream.WriteTo(stream);
23 stream?.Close();
24 }
25 catch (Exception ex)
26 {
27 WebApiResponse.Fail("文件上傳中出錯:" + ex.Message);
28 }
29
30 return response;
31 }
3、多線程完成後通知
DirectoryInfo di = new DirectoryInfo(mergeDirPath);
var files = di.GetFiles().OrderBy(f => f.Name).ToList();
foreach (var item in files)
{
using var stream = new FileStream(filePath, FileMode.Append);
using var fs = item.OpenRead();
fs.WriteTo(stream);
fs?.Close();
stream?.Close();
}
客戶端多線程上傳
NeedAddFiles爲要上傳的文件集合,按分頁的思想,每一頁的列表上傳,分給一個task,頁內是一個一個文件上傳的,多頁是併發上傳的。
while (true)
{
var sources = NeedAddFiles.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
if (sources.Count == 0) break;
pageIndex++;
var task = Task.Run(function: async () =>
{
foreach (var item in sources)
{
await FileUpload(item.Key, item.Value);
await Task.Delay(5);
}
});
tasks.Add(task);
}
//等待所有線程完成
await Task.WhenAll(tasks).ContinueWith(t =>
{
Console.WriteLine("多線程上傳任務已完成");
});
Console.WriteLine("等待線程已完成");