/// <summary> /// 创建块(携带首片数据),同时检查CRC32 /// </summary> /// <param name="resumeBlockerObj">创建分片上次的块请求</param> private async Task MakeBlock(object resumeBlockerObj) { var resumeBlocker = (ResumeBlocker)resumeBlockerObj; var doneEvent = resumeBlocker.DoneEvent; var blockMakeResults = resumeBlocker.BlockMakeResults; var putExtra = resumeBlocker.PutExtra; var blockIndex = resumeBlocker.BlockIndex; var result = new HttpResult(); //check whether to cancel while (true) { var upCtl = resumeBlocker.PutExtra.UploadController(); if (upCtl == UploadControllerAction.Suspended) { doneEvent.WaitOne(1000); } else if (upCtl == UploadControllerAction.Aborted) { doneEvent.Set(); result.Code = (int)HttpCode.USER_CANCELED; result.RefCode = (int)HttpCode.USER_CANCELED; result.RefText += $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}] [ResumableUpload] Info: upload task is aborted, mkblk {blockIndex}\n"; blockMakeResults.Add(blockIndex, result); return; } else { break; } } var blockBuffer = resumeBlocker.BlockBuffer; var blockSize = blockBuffer.Length; var upToken = resumeBlocker.UploadToken; var uploadedBytesDict = resumeBlocker.UploadedBytesDict; var fileSize = resumeBlocker.FileSize; var progressLock = resumeBlocker.ProgressLock; var resumeInfo = resumeBlocker.ResumeInfo; try { //get upload host var ak = UpToken.GetAccessKeyFromUpToken(upToken); var bucket = UpToken.GetBucketFromUpToken(upToken); if (ak == null || bucket == null) { result = HttpResult.InvalidToken; doneEvent.Set(); return; } var uploadHost = await _config.UpHost(ak, bucket); var url = $"{uploadHost}/mkblk/{blockSize}"; var upTokenStr = $"UpToken {upToken}"; using (var ms = new MemoryStream(blockBuffer, 0, blockSize)) { var data = ms.ToArray(); result = await _httpManager.PostDataAsync(url, data, token : upTokenStr); if (result.Code == (int)HttpCode.OK) { var rc = JsonConvert.DeserializeObject <ResumeContext>(result.Text); if (rc.Crc32 > 0) { var crc1 = rc.Crc32; var crc2 = Crc32.CheckSumSlice(blockBuffer, 0, blockSize); if (crc1 != crc2) { result.RefCode = (int)HttpCode.USER_NEED_RETRY; result.RefText += $" CRC32: remote={crc1}, local={crc2}\n"; } else { //write the mkblk context resumeInfo.Contexts[blockIndex] = rc.Ctx; resumeInfo.ExpiredAt = rc.ExpiredAt; lock (progressLock) { uploadedBytesDict["UploadProgress"] += blockSize; } putExtra.ProgressHandler(uploadedBytesDict["UploadProgress"], fileSize); } } else { result.RefText += $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}] JSON Decode Error: text = {result.Text}"; result.RefCode = (int)HttpCode.USER_NEED_RETRY; } } else { result.RefCode = (int)HttpCode.USER_NEED_RETRY; } } } catch (Exception ex) { var sb = new StringBuilder(); sb.Append($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.ffff}] mkblk Error: "); var e = ex; while (e != null) { sb.Append(e.Message + " "); e = e.InnerException; } sb.AppendLine(); if (ex is QiniuException qex) { result.Code = qex.HttpResult.Code; result.RefCode = qex.HttpResult.Code; result.Text = qex.HttpResult.Text; result.RefText += sb.ToString(); } else { result.RefCode = (int)HttpCode.USER_UNDEF; result.RefText += sb.ToString(); } } //return the http result blockMakeResults.Add(blockIndex, result); doneEvent.Set(); }