單執行緒普通上傳
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:分片大小
說明:可以看到,此方法與上傳方法比,少了很多引數。因為它只是通知伺服器已經結束,不攜帶檔案資料。
由於多執行緒,最後一個分片,不一定最後傳完,所以伺服器無法判斷上傳是否結束。針對這個問題的解決方案,客戶端多呼叫一個介面,通知伺服器,我傳輸完畢,你可以合併分片檔案。
以上是我的斷點續傳的實踐,專案已經結束,所以做個總結,供大家參考。