上傳——斷點續傳之實踐篇

micDavid發表於2021-06-17

單執行緒普通上傳

1、用流開啟檔案

 var item = new FileInfo(filePath);
 FileStream stream = item.OpenRead();

 

2、讀取到位元組

var fs=stream;

var transeBytesSize = fs.Length;

var data = new byte[(int)transeBytesSize];

BinaryReader bReader = new BinaryReader(fs);

bReader.Read(data, 0, (int)transeBytesSize);

 

3、呼叫服務端上傳介面

var uploadResult = await WebApi.UploadByTrunk(doc, data, md5, fileLength, transeBytesSize, chunkSize);

此方法是一個非同步的上傳方法,後面會多次用到,介面引數說明:

doc:檔案相關的資訊

data:傳輸的位元組

md5:檔案的md5

fileLength:檔案大小

transeBytesSize:當前要傳輸的位元組大小

chunkSize:分片大小

單執行緒斷點續傳

1、獲取上次傳輸的斷點位置,通過服務端介面獲取

 var startResult = await WebApi.GetLastUploadSize(doc.Id, fileLength, fileId);

 var startPoint = startResult.Results;

 

介面引數說明:

doc.Id 文件Id

fileLength:檔案長度

fileId:檔案Id,guid型別

 

2、定位流的當前位置

定位流的目的是跳過已經已經上傳的位元組,startPoint就是斷點所在,所以跳過startPoint個位元組,再上傳。

if (startPoint >= 0 && startPoint <= fileLength - 1)
{
   fs.Seek(startPoint, SeekOrigin.Current);
}

 

3、分片傳輸

   int i = 0;

   var totalChunks = leftChunkSize % chunkSize == 0 ? leftChunkSize / chunkSize : leftChunkSize / chunkSize + 1;

   for (; startPoint <= fileLength - 1; startPoint += transeBytesSize)
   {
     var leftChunkSize = fileLength - startPoint;
     var transeBytesSize = leftChunkSize > chunkSize ? chunkSize : leftChunkSize;

     var data = new byte[(int)transeBytesSize];
     bReader.Read(data, 0, (int)transeBytesSize);
     i++;
     var uploadResult = await WebApi.UploadByTrunk(doc, data, md5, fileLength, transeBytesSize, chunkSize, mulThreadEnable, totalChunks, i);                    
   }

 

上傳介面引數補充,參考普通上傳中的介面:

mulThreadEnable:是否啟用多執行緒

totalChunks:總分片數

i:當前分片序號,表示第幾個分片,傳輸當前片序號的目的,在於讓伺服器知道上傳是否結束。伺服器知道後,可以按順序合併分片檔案。

     

多執行緒斷點續傳   

1、獲取上次傳輸的分片數,通過服務端介面獲取

 var lastChunks = await WebApi.GetLastChunks(doc.Id, fileId, chunkSize);

 if (lastChunks != null)
 {
     hasChunks = lastChunks.Results;
 }

 

2、準備好分片資料

 List<Task<WebApiResponse>> tasks = new List<Task<WebApiResponse>>();

 int i = 0;

 Dictionary<int, byte[]> datas = new Dictionary<int, byte[]>();
 Dictionary<int, long> transeBytesSizeDic = new Dictionary<int, long>();


 for (; startPoint <= fileLength - 1; startPoint += transeBytesSize)
 {
   i++;

   leftChunkSize = fileLength - startPoint;

   transeBytesSize = leftChunkSize > chunkSize ? chunkSize : leftChunkSize;

   var data = new byte[(int)transeBytesSize];

   bReader.Read(data, 0, (int)transeBytesSize);

   if (hasChunks != null && hasChunks.Count > 0 && hasChunks.Contains(i)) continue;

   datas.Add(i, data);

   transeBytesSizeDic.Add(i, transeBytesSize);

 }   

 

3、上傳分片資料

 for (int j = 1; j <= totalChunks; j++)
 {
    int k = j;
    if (!datas.ContainsKey(k)) continue;   //跳過已經上傳的分片
    var task = WebApi.UploadByTrunk(doc, datas[k], md5, fileLength, transeBytesSizeDic[k], chunkSize, mulThreadEnable, datas.Count, k);
    tasks.Add(task);
  }      

 

4、等待任務完成後,傳送結束標識

Task.WaitAll(tasks.ToArray());     

 foreach (var item in tasks)
 {

    var allResult = item.Result;

    //處理上傳後的結果,如判斷成功與否等                                                        

    //此處為實際的業務邏輯                  

  }

  var sendResult = await WebApi.SendFinish(doc, md5, totalChunks, fileLength, chunkSize); 

 

介面引數說明:

doc:檔案相關的資訊

md5:檔案的md5

fileLength:檔案大小

totalChunks:總分片大小

chunkSize:分片大小

說明:可以看到,此方法與上傳方法比,少了很多引數。因為它只是通知伺服器已經結束,不攜帶檔案資料。

由於多執行緒,最後一個分片,不一定最後傳完,所以伺服器無法判斷上傳是否結束。針對這個問題的解決方案,客戶端多呼叫一個介面,通知伺服器,我傳輸完畢,你可以合併分片檔案。

以上是我的斷點續傳的實踐,專案已經結束,所以做個總結,供大家參考。    

相關文章