private void GetObject() { if (getObjectRequest == null) { getObjectRequest = new GetObjectRequest(bucket, key, localDir, localFileName); } if (progressCallback != null) { getObjectRequest.SetCosProgressCallback(progressCallback); } getObjectRequest.SetRange(rangeStart, rangeEnd); getObjectRequest.SetLocalFileOffset(localFileOffset); cosXmlServer.GetObject(getObjectRequest, delegate(CosResult result) { lock (syncExit) { if (isExit) { return; } } if (UpdateTaskState(TaskState.Completed)) { GetObjectResult getObjectResult = result as GetObjectResult; DownloadTaskResult downloadTaskResult = new DownloadTaskResult(); downloadTaskResult.SetResult(getObjectResult); if (successCallback != null) { successCallback(downloadTaskResult); } } } , delegate(CosClientException clientEx, CosServerException serverEx) { lock (syncExit) { if (isExit) { return; } } if (UpdateTaskState(TaskState.Failed)) { if (failCallback != null) { failCallback(clientEx, serverEx); } } }); }
// actual get object requests with concurrency control private void GetObject(string crc64ecma) { lock (syncExit) { if (isExit) { return; } } // create dir if not exist DirectoryInfo dirInfo = new DirectoryInfo(localDir); if (!dirInfo.Exists) { dirInfo.Create(); } // concurrency control AutoResetEvent resetEvent = new AutoResetEvent(false); int retries = 0; // return last response GetObjectResult downloadResult = null; if (sliceToRemove == null) { sliceToRemove = new HashSet <int>(); } while (sliceList.Count != 0 && retries < maxRetries) { retries += 1; foreach (int partNumber in sliceList.Keys) { if (sliceToRemove.Contains(partNumber)) { continue; } DownloadSliceStruct slice; bool get_state = sliceList.TryGetValue(partNumber, out slice); if (activeTasks >= maxTasks) { resetEvent.WaitOne(); } lock (syncExit) { if (isExit) { return; } } string tmpFileName = "." + localFileName + ".cosresumable." + slice.partNumber; // clear remainance FileInfo tmpFileInfo = new FileInfo(localDir + tmpFileName); if (tmpFileInfo.Exists && tmpFileInfo.Length != (slice.sliceEnd - slice.sliceStart + 1) && localFileOffset != 0) { System.IO.File.Delete(localDir + tmpFileName); } getObjectRequest = new GetObjectRequest(bucket, key, localDir, tmpFileName); tmpFilePaths.Add(localDir + tmpFileName); getObjectRequest.SetRange(slice.sliceStart, slice.sliceEnd); if (progressCallback != null && this.sliceList.Count == 1) { getObjectRequest.SetCosProgressCallback(delegate(long completed, long total) { progressCallback(completed, total); } ); } Interlocked.Increment(ref activeTasks); cosXmlServer.GetObject(getObjectRequest, delegate(CosResult result) { Interlocked.Decrement(ref activeTasks); lock (syncExit) { if (isExit) { return; } } sliceToRemove.Add(partNumber); if (progressCallback != null && this.sliceList.Count > 1) { long completed = sliceToRemove.Count * this.sliceSize; long total = rangeEnd - rangeStart; if (completed > total) { completed = total; } progressCallback(completed, total); } downloadResult = result as GetObjectResult; resetEvent.Set(); if (resumable) { // flush done parts this.resumableInfo.slicesDownloaded.Add(slice); this.resumableInfo.Persist(resumableTaskFile); } }, delegate(CosClientException clientEx, CosServerException serverEx) { Interlocked.Decrement(ref activeTasks); lock (syncExit) { if (isExit) { return; } } // server 4xx throw and stop if (serverEx != null && serverEx.statusCode < 500) { throw serverEx; if (failCallback != null) { failCallback(null, serverEx); } return; } // client cannot connect, just retry if (clientEx != null) { gClientExp = clientEx; } resetEvent.Set(); } ); } while (activeTasks != 0) { Thread.Sleep(100); } // remove success parts foreach (int partNumber in sliceToRemove) { sliceList.Remove(partNumber); } } if (this.sliceList.Count != 0) { if (gClientExp != null) { throw gClientExp; } COSXML.CosException.CosClientException clientEx = new COSXML.CosException.CosClientException ((int)CosClientError.InternalError, "max retries " + retries + " excceed, download fail"); throw clientEx; if (UpdateTaskState(TaskState.Failed)) { if (failCallback != null) { failCallback(clientEx, null); } } return; } // file merge FileMode fileMode = FileMode.OpenOrCreate; FileInfo localFileInfo = new FileInfo(localDir + localFileName); if (localFileInfo.Exists && localFileOffset == 0 && localFileInfo.Length != rangeEnd - rangeStart + 1) { fileMode = FileMode.Truncate; } using (var outputStream = File.Open(localDir + localFileName, fileMode)) { outputStream.Seek(localFileOffset, 0); // sort List <string> tmpFileList = new List <string>(this.tmpFilePaths); tmpFileList.Sort(delegate(string x, string y){ int partNumber1 = int.Parse(x.Split(new string[] { "cosresumable." }, StringSplitOptions.None)[1]); int partNumber2 = int.Parse(y.Split(new string[] { "cosresumable." }, StringSplitOptions.None)[1]); return(partNumber1 - partNumber2); }); foreach (var inputFilePath in tmpFileList) { // tmp not exist, clear everything and ask for retry if (!File.Exists(inputFilePath)) { // check if download already completed if (File.Exists(localDir + localFileName)) { FileInfo fileInfo = new FileInfo(localDir + localFileName); if (fileInfo.Length == rangeEnd - rangeStart + 1) { foreach (var tmpFile in tmpFileList) { System.IO.File.Delete(tmpFile); } if (resumableTaskFile != null) { System.IO.File.Delete(resumableTaskFile); } break; } } // not completed, report fatal error foreach (var tmpFile in tmpFileList) { System.IO.File.Delete(tmpFile); } if (resumableTaskFile != null) { System.IO.File.Delete(resumableTaskFile); } if (File.Exists(localDir + localFileName)) { System.IO.File.Delete(localDir + localFileName); } COSXML.CosException.CosClientException clientEx = new COSXML.CosException.CosClientException ((int)CosClientError.InternalError, "local tmp file not exist, could be concurrent writing same file" + inputFilePath + " download again"); throw clientEx; if (failCallback != null) { failCallback(clientEx, null); } break; } using (var inputStream = File.OpenRead(inputFilePath)) { FileInfo info = new FileInfo(inputFilePath); inputStream.CopyTo(outputStream); } } tmpFileList.Clear(); tmpFilePaths.Clear(); if (UpdateTaskState(TaskState.Completed)) { var dir = new DirectoryInfo(localDir); foreach (var file in dir.EnumerateFiles("." + localFileName + ".cosresumable.*")) { file.Delete(); } if (resumableTaskFile != null) { FileInfo info = new FileInfo(resumableTaskFile); if (info.Exists) { info.Delete(); } } DownloadTaskResult downloadTaskResult = new DownloadTaskResult(); downloadTaskResult.SetResult(downloadResult); outputStream.Close(); if (successCallback != null) { successCallback(downloadTaskResult); } return; } else { // 容灾 return DownloadTaskResult downloadTaskResult = new DownloadTaskResult(); downloadTaskResult.SetResult(downloadResult); outputStream.Close(); if (successCallback != null) { successCallback(downloadTaskResult); } } } return; }