private async Task <UploadResponse> SingleUpload(UploadRequest request)
        {
            logger.Info("Using PutObject to upload file");

            if (_configuration.EnforceMd5Upload && request.PutObjectRequest.ContentMD5 == null)
            {
                var inputStream = request.PutObjectRequest.PutObjectBody;
                var contentMd5  = MultipartUtils.CalculateMd5(inputStream);
                if (inputStream.CanSeek)
                {
                    inputStream.Position = 0;
                }
                else
                {
                    throw new NotSupportedException("Stream cannot be seeked, Please re-try with re-readable streams.");
                }
                request.PutObjectRequest.ContentMD5 = contentMd5;
            }

            if (request.OnProgress != null)
            {
                request.PutObjectRequest.PutObjectBody = new ProgressTrackingInputStream(
                    request.PutObjectRequest.PutObjectBody, new ProgressTracker(request.OnProgress, request.PutObjectRequest.PutObjectBody.Length));
            }

            var response = await _osClient.PutObject(request.PutObjectRequest, request.RetryConfiguration).ConfigureAwait(false);

            return(new UploadResponse()
            {
                ETag = response.ETag,
                OpcRequestId = response.OpcRequestId,
                OpcClientRequestId = response.OpcClientRequestId,
                OpcContentMd5 = response.OpcContentMd5
            });
        }
        /// <summary>
        /// Add the next part to the upload. Parts will be committed in the order submitted.
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="contentLength"></param>
        /// <param name="partNumber"></param>
        /// <returns>Part Number</returns>
        public async Task <int> AddPart(Stream stream, long contentLength, int partNumber)
        {
            ValidateState();
            MultipartUtils.ValidatePartNumber(partNumber);

            var request = new UploadPartRequest()
            {
                BucketName         = _bucketName,
                NamespaceName      = _namespaceName,
                ObjectName         = _objectName,
                ContentLength      = contentLength,
                UploadId           = _multipartManifest.UploadId,
                UploadPartNum      = partNumber,
                UploadPartBody     = stream,
                OpcClientRequestId = CreateClientRequestId($"Part{partNumber}")
            };

            if (_enforceContentMD5Upload)
            {
                request.ContentMD5 = MultipartUtils.CalculateMd5(stream);
                if (stream.CanSeek)
                {
                    stream.Position = 0;
                    _logger.Info($"MD5: {request.ContentMD5}");
                }
                else
                {
                    throw new NotSupportedException("Stream cannot be seeked, Please re-try with re-readable streams.");
                }
            }

            await _transferManager.StartTransfer(request).ConfigureAwait(false);

            return(partNumber);
        }
        /// <summary>
        /// Initiates a new upload request.  The upload manager will decide whether to use
        /// a single PutObject call or multi-part uploads depending on the UploadConfiguration specified.
        /// If a multi-part upload attempt fails, the UploadManager will attempt to abort the upload to avoid leaving
        /// partially complete uploads and parts (unless explicitly disabled via UploadConfiguration).
        /// </summary>
        /// <param name="request">UploadRequest</param>
        /// <returns>Task<UploadResponse></returns>
        public async Task <UploadResponse> Upload(UploadRequest request)
        {
            if (MultipartUtils.ShouldUseMultipart(_configuration, request.PutObjectRequest.PutObjectBody.Length))
            {
                logger.Info("Using multipart Upload");
                return(await MultipartUpload(request).ConfigureAwait(false));
            }

            return(await SingleUpload(request).ConfigureAwait(false));
        }