上傳——斷點續傳之實踐篇
單線程普通上傳
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:分片大小
說明:可以看到,此方法與上傳方法比,少了很多參數。因為它只是通知服務器已經結束,不攜帶文件數據。
由於多線程,最後一個分片,不一定最後傳完,所以服務器無法判斷上傳是否結束。針對這個問題的解決方案,客戶端多調用一個接口,通知服務器,我傳輸完畢,你可以合併分片文件。
以上是我的斷點續傳的實踐,項目已經結束,所以做個總結,供大家參考。