Ejemplo n.º 1
0
        /// <summary>
        /// Transfer data from the source stream to the destination stream. This method also performs the necessary
        /// data transforms (throttling, hashing, encrypting, etc) as the data is streamed. This allows a the
        /// source stream to only be read a single time while performing multiple operations on the data.
        /// </summary>
        /// <param name="sourceStream">The stream that the data is read from</param>
        /// <param name="destinationStream">The stream that the data is written to</param>
        /// <returns>(async) The result of the transfer</returns>
        private async Task <TransferResult> TransferDataWithTransformsAsync(
            Stream sourceStream,
            Stream destinationStream)
        {
            SHA1Cng sha1 = null;
            MD5Cng  md5  = null;

            try
            {
                // By default, we will compute the SHA1 and MD5 hashes of the file as it is streamed.
                sha1 = new SHA1Cng();
                md5  = new MD5Cng();

                // Allocate the buffer that will be used to transfer the data. The source stream will be read one
                // suffer-size as a time, then written to the destination.
                byte[] buffer       = new byte[TransferBufferSize];
                long   readTotal    = 0;
                long   writtenTotal = 0;

                while (true)
                {
                    // If we are using a throttling manager, get the necessary number of tokens to
                    // transfer the data
                    if (this.throttlingManager != null)
                    {
                        int tokens = 0;
                        while (true)
                        {
                            // Get the number of tokens needed. We will require tokens equaling the number of
                            // bytes to be transferred. This is a non-blocking calling and will return between
                            // 0 and the number of requested tokens.
                            tokens += this.throttlingManager.GetTokens(TransferBufferSize - tokens);
                            if (tokens >= TransferBufferSize)
                            {
                                // We have enough tokens to transfer the buffer
                                break;
                            }

                            // We don't (yet) have enough tokens, so wait for a short duration and try again
                            await Task.Delay(10, this.cancellationToken).ConfigureAwait(false);
                        }
                    }

                    // Read data from the source adapter
                    int read = sourceStream.Read(buffer, 0, buffer.Length);

                    CounterManager.LogSyncJobCounter(
                        "SyncJob/BytesRead",
                        read);

                    // Increment the total number of bytes read from the source adapter
                    readTotal += read;
                    int bytesWritten;

                    if (read < buffer.Length)
                    {
                        // Compute the last part of the SHA1 and MD5 hashes (this finished the algorithm's work).
                        sha1.TransformFinalBlock(buffer, 0, read);
                        md5.TransformFinalBlock(buffer, 0, read);

                        if (this.encryptionManager != null)
                        {
                            bytesWritten = this.encryptionManager.TransformFinalBlock(buffer, 0, read);
                        }
                        else
                        {
                            destinationStream.Write(buffer, 0, read);
                            destinationStream.Flush();
                            bytesWritten = buffer.Length;
                        }

                        CounterManager.LogSyncJobCounter(
                            "SyncJob/BytesWritten",
                            read);

                        writtenTotal += bytesWritten;

                        // Increment the total number of bytes written to the desination adapter
                        this.bytesCompleted += read;

                        this.progressChanged(new CopyProgressInfo(this.bytesCompleted, this.updateInfo));

                        // Read the end of the stream
                        break;
                    }

                    // Pass the data through the required hashing algorithms.
                    sha1.TransformBlock(buffer, 0, read, buffer, 0);
                    md5.TransformBlock(buffer, 0, read, buffer, 0);

                    // Write the data to the destination adapter
                    if (this.encryptionManager != null)
                    {
                        bytesWritten = this.encryptionManager.TransformBlock(buffer, 0, read);
                    }
                    else
                    {
                        destinationStream.Write(buffer, 0, read);
                        bytesWritten = buffer.Length;
                    }

                    CounterManager.LogSyncJobCounter(
                        "SyncJob/BytesWritten",
                        read);

                    writtenTotal += bytesWritten;

                    // Increment the total number of bytes written to the desination adapter
                    this.bytesCompleted += read;

                    if (this.syncProgressUpdateStopwatch.ElapsedMilliseconds > 100)
                    {
                        this.progressChanged(new CopyProgressInfo(this.bytesCompleted, this.updateInfo));

                        // After reporting the number of bytes copied for this file, set back to 0 so that we are only
                        // reporting the number of bytes sync we last invoked the callback.
                        this.bytesCompleted = 0;

                        this.syncProgressUpdateStopwatch.Restart();
                    }
                }

                TransferResult result = new TransferResult
                {
                    BytesRead    = readTotal,
                    BytesWritten = writtenTotal
                };

                if (this.encryptionManager != null)
                {
                    if (this.encryptionManager.Mode == EncryptionMode.Encrypt)
                    {
                        result.Sha1Hash            = sha1.Hash;
                        result.Md5Hash             = md5.Hash;
                        result.TransformedSha1Hash = this.encryptionManager.Sha1Hash;
                        result.TransformedMd5Hash  = this.encryptionManager.Md5Hash;
                    }
                    else
                    {
                        result.TransformedSha1Hash = sha1.Hash;
                        result.TransformedMd5Hash  = md5.Hash;
                        result.Sha1Hash            = this.encryptionManager.Sha1Hash; // The SHA1 hash of the data written by the encryption manager
                        result.Md5Hash             = this.encryptionManager.Md5Hash;
                    }
                }
                else
                {
                    result.Sha1Hash = sha1.Hash;
                    result.Md5Hash  = md5.Hash;
                }

                return(result);
            }
            finally
            {
                sha1?.Dispose();
                md5?.Dispose();
            }
        }