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

單線程普通上傳

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:分片大小

說明:可以看到,此方法與上傳方法比,少了很多參數。因爲它只是通知服務器已經結束,不攜帶文件數據。

由於多線程,最後一個分片,不一定最後傳完,所以服務器無法判斷上傳是否結束。針對這個問題的解決方案,客戶端多調用一個接口,通知服務器,我傳輸完畢,你可以合併分片文件。

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章