Task <FileState> UploadSlice(
            string sessionId,
            long offset,
            Stream dataStream,
            IProgress <AVUploadProgressEventArgs> progress,
            CancellationToken cancellationToken)
        {
            long dataLength = dataStream.Length;

            if (progress != null)
            {
                lock (mutex)
                {
                    progress.Report(new AVUploadProgressEventArgs()
                    {
                        Progress = AVFileController.CalcProgress(offset, dataLength)
                    });
                }
            }

            if (offset == dataLength)
            {
                return(Task.FromResult <FileState>(fileState));
            }

            var sliceFile = GetNextBinary(offset, dataStream);

            return(ExcuteUpload(sessionId, offset, sliceFile, cancellationToken).OnSuccess(_ =>
            {
                offset += sliceFile.Length;
                if (offset == dataLength)
                {
                    done = true;
                    return Task.FromResult <FileState>(fileState);
                }
                var response = _.Result.Item2;
                var resumeData = response["data"] as IDictionary <string, object>;
                var sliceSession = resumeData["session"].ToString();
                return UploadSlice(sliceSession, offset, dataStream, progress, cancellationToken);
            }).Unwrap());
        }
        public Task <FileState> SaveAsync(FileState state,
                                          Stream dataStream,
                                          string sessionToken,
                                          IProgress <AVUploadProgressEventArgs> progress,
                                          CancellationToken cancellationToken)
        {
            if (state.Url != null)
            {
                return(Task <FileState> .FromResult(state));
            }
            fileState = state;
            data      = dataStream;
            return(AVFileController.GetFileToken(fileState, cancellationToken).OnSuccess(_ =>
            {
                var fileToken = _.Result.Item2;
                uploadUrl = fileToken["upload_url"].ToString();
                token = fileToken["token"].ToString();
                fileState.ObjectId = fileToken["objectId"].ToString();
                bucket = fileToken["bucket"].ToString();

                return FileSlice(cancellationToken).OnSuccess(t =>
                {
                    if (done)
                    {
                        return Task <FileState> .FromResult(state);
                    }
                    var response = t.Result.Item2;
                    var resumeData = response["data"] as IDictionary <string, object>;
                    if (resumeData.ContainsKey("access_url"))
                    {
                        return Task <FileState> .FromResult(state);
                    }
                    var sliceSession = resumeData["session"].ToString();
                    var sliceOffset = long.Parse(resumeData["offset"].ToString());
                    return UploadSlice(sliceSession, sliceOffset, dataStream, progress, cancellationToken);
                }).Unwrap();
            }).Unwrap());
        }
        Task UploadNextChunk(FileState state, Stream dataStream, string context, long offset, IProgress <AVUploadProgressEventArgs> progress)
        {
            if (AVClient.fileUploaderDebugLog)
            {
                AVClient.LogTracker(state.counter + "|completed=" + state.completed + "|total=" + dataStream.Length + "|");
            }
            var totalSize     = dataStream.Length;
            var remainingSize = totalSize - state.completed;

            if (progress != null)
            {
                lock (mutex)
                {
                    progress.Report(new AVUploadProgressEventArgs()
                    {
                        Progress = AVFileController.CalcProgress(state.completed, totalSize)
                    });
                }
            }
            if (state.completed == totalSize)
            {
                return(QiniuMakeFile(state, state.frozenData, state.token, state.CloudName, totalSize, state.block_ctxes.ToArray(), CancellationToken.None));
            }
            else if (state.completed % BLOCKSIZE == 0)
            {
                var firstChunkBinary = GetChunkBinary(state.completed, dataStream);

                var blockSize = remainingSize > BLOCKSIZE ? BLOCKSIZE : remainingSize;
                return(MakeBlock(state, firstChunkBinary, blockSize).ContinueWith(t =>
                {
                    var dic = AVClient.ReponseResolve(t.Result, CancellationToken.None);
                    var ctx = dic.Item2["ctx"].ToString();

                    offset = long.Parse(dic.Item2["offset"].ToString());
                    var host = dic.Item2["host"].ToString();

                    state.completed += firstChunkBinary.Length;
                    if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize)
                    {
                        state.block_ctxes.Add(ctx);
                    }
                    //if (AVClient.fileUploaderDebugLog)
                    //{
                    //    AVClient.LogTracker(AVFile.objectCounter + "|completed=" + state.completed + "stream:position=" + dataStream.Position + "|");
                    //}

                    return UploadNextChunk(state, dataStream, ctx, offset, progress);
                }).Unwrap());
            }
            else
            {
                var chunkBinary = GetChunkBinary(state.completed, dataStream);
                return(PutChunk(state, chunkBinary, context, offset).ContinueWith(t =>
                {
                    var dic = AVClient.ReponseResolve(t.Result, CancellationToken.None);
                    var ctx = dic.Item2["ctx"].ToString();

                    offset = long.Parse(dic.Item2["offset"].ToString());
                    var host = dic.Item2["host"].ToString();
                    state.completed += chunkBinary.Length;
                    if (state.completed % BLOCKSIZE == 0 || state.completed == totalSize)
                    {
                        state.block_ctxes.Add(ctx);
                    }
                    //if (AVClient.fileUploaderDebugLog)
                    //{
                    //    AVClient.LogTracker(state.counter + "|completed=" + state.completed + "stream:position=" + dataStream.Position + "|");
                    //}

                    return UploadNextChunk(state, dataStream, ctx, offset, progress);
                }).Unwrap());
            }
        }