I'm having this piece of code that computes the MD5 hash for a given input file.
public static String ComputeMD5(String filename)
{
using (var md5 = MD5.Create())
{
try
{
using (var stream = File.OpenRead(filename))
{
return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "").ToLower();
}
}
catch (Exception)
{
// File is not accessible, return String.Empty
return String.Empty;
}
}
}
I'm running this time consuming operation in a separate thread. For very big files this operation may take some seconds/minutes. What I want to do is to be able to stop the operation from another thread, for example using a "Stop" button in the GUI. Any suggestions?
You can read file parts and apply MD5.TransformBlock to each read part. (Notice, that last part should be read with MD5.TransformFinalBlock).
Between processing each block you can check if cancellation is required, you are free to use any synchronization primitives you like.
Here is example, that uses CancellationToken:
using System;
using System.IO;
using System.Threading;
using System.Security.Cryptography;
namespace Stack
{
class Program
{
static void Main(string[] args)
{
using (var cancellationTokenSource = new CancellationTokenSource())
{
var thread = new Thread(() =>
{
try
{
var hash = CalcHash("D:/Image.iso", cancellationTokenSource.Token);
Console.WriteLine($"Done: hash is {BitConverter.ToString(hash)}");
}
catch (OperationCanceledException)
{
Console.WriteLine("Canceled :(");
}
});
// Start background thread
thread.Start();
Console.WriteLine("Working, press any key to exit");
Console.ReadLine();
cancellationTokenSource.Cancel();
}
Console.WriteLine("Finished");
Console.ReadLine();
}
static byte[] CalcHash(string path, CancellationToken ct)
{
using (var stream = File.OpenRead(path))
using (var md5 = MD5.Create())
{
const int blockSize = 1024 * 1024 * 4;
var buffer = new byte[blockSize];
long offset = 0;
while (true)
{
ct.ThrowIfCancellationRequested();
var read = stream.Read(buffer, 0, blockSize);
if (stream.Position == stream.Length)
{
md5.TransformFinalBlock(buffer, 0, read);
break;
}
offset += md5.TransformBlock(buffer, 0, buffer.Length, buffer, 0);
Console.WriteLine($"Processed {offset * 1.0 / 1024 / 1024} MB so far");
}
return md5.Hash;
}
}
}
}
Related
Based on this example: https://github.com/dotnet/runtime/issues/28968 I'm writing app to communicate with device over serial port, based on protocol document.
This is my code:
using System;
using System.Text;
using System.Threading.Tasks;
namespace TN10
{
public class TN10
{
public static void Main()
{
Console.WriteLine(ByteArrayToString(ResponseData().Result));
}
public static async Task<byte[]> GetResponseData()
{
byte[] hextosend = {0x06, 0x30, 0x30};
var bytestosend = SendReceive.GetBytesToSend(hextosend);
var timeout = new TimeSpan(1000000000);
var sendTask = Task.Run(() => new PortChat().SendMessage(bytestosend, timeout));
try
{
await Task.WhenAny(sendTask, Task.Delay(timeout));
}
catch (TaskCanceledException)
{
throw new TimeoutException();
}
var response = await sendTask;
return response;
}
public static async Task<byte[]> ResponseData()
{
var data = await GetResponseData();
return data;
}
public static string ByteArrayToString(byte[] ba)
{
StringBuilder hex = new StringBuilder(ba.Length * 2);
foreach (byte b in ba)
hex.AppendFormat("{0:x2}", b);
return hex.ToString();
}
}
}
and
using System;
using System.Diagnostics;
using System.IO.Ports;
namespace TN10
{
public class PortChat
{
private readonly object _syncRoot = new object();
public SerialPort Port = new SerialPort(new Config().ComPort, 115200, Parity.None, 8, StopBits.One);
public byte[] SendMessage(byte[] message, TimeSpan timeout)
{
// Use stopwatch to update SerialPort.ReadTimeout and SerialPort.WriteTimeout
// as we go.
var stopwatch = Stopwatch.StartNew();
// Organize critical section for logical operations using some standard .NET tool.
lock (_syncRoot)
{
var originalWriteTimeout = Port.WriteTimeout;
var originalReadTimeout = Port.ReadTimeout;
try
{
// Start logical request.
Port.WriteTimeout = (int) Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0);
Port.Open();
Port.Write(message, 0, message.Length);
// Expected response length. Look for the constant value from
// the device communication protocol specification or extract
// from the response header (first response bytes) if there is
// any specified in the protocol.
var count = 17;
var buffer = new byte[count];
var offset = 0;
// Loop until we recieve a full response.
while (count > 0)
{
Port.ReadTimeout = (int) Math.Max((timeout - stopwatch.Elapsed).TotalMilliseconds, 0);
var readCount = Port.Read(buffer, offset, count);
offset += readCount;
count -= readCount;
}
return buffer;
}
finally
{
// Restore SerialPort state.
Port.ReadTimeout = originalReadTimeout;
Port.WriteTimeout = originalWriteTimeout;
}
}
}
}
}
Problem is the code simply does not return bytes read from port.
I see them if I put breakpoint on this line
var readCount = Port.Read(buffer, offset, count);
but nothing "after".
Thanks.
How can i use Calculate MD5 Hash as Async?
I have this code:
static string CalculateMD5(string filename)
{
using (var md5 = MD5.Create())
{
using (var stream = File.OpenRead(filename))
{
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
}
}
}
I want to hash a file of 1GB and then it freezes my app for 20 sec.
As you are reading from a file you can use IO-bound asynchronicity. Unfortunarely neither the File class nor the MD5 class support it out of the box. But it can be manually done:
static async Task<string> CalculateMD5Async(string filename)
{
using (var md5 = System.Security.Cryptography.MD5.Create())
{
using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true)) // true means use IO async operations
{
byte[] buffer = new byte[4096];
int bytesRead;
do
{
bytesRead = await stream.ReadAsync(buffer, 0, 4096);
if (bytesRead > 0)
{
md5.TransformBlock(buffer, 0, bytesRead, null, 0);
}
} while (bytesRead > 0);
md5.TransformFinalBlock(buffer, 0, 0);
return BitConverter.ToString(md5.hash).Replace("-", "").ToUpperInvariant();
}
}
For performance reasons you may want to increase the buffer size.
Try this:
private async void CalculateMd5_Click(object sender, EventArgs e)
{
var filePath = "Path/to/your/file";
var md5hash = await CalculateMd5ForFileAsync(filePath);
}
private Task<string> CalculateMd5ForFileAsync(string filePath)
{
return new Task<string>(() => ComputeMd5HashForFile(filePath));
}
private string ComputeMd5HashForFile(string filePath)
{
using (var md5 = MD5.Create())
{
using (var stream = File.OpenRead(filePath))
{
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToUpperInvariant();
}
}
}
I am implementing C# code in which I am trying to transcribe audio more than of 100 mb but it is not allowing me to develop that required a program that can send more than 100 mb audio in C#
In this code I am using web socket but how I can send like streaming a audio
public static void CallWatson()
{
using (var nf = new Notifier())
using (var ws = new WebSocket("wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize?watson-token=""))
{
string startActionjson = "{\"action\": \"start\", \"content-type\": \"audio/wav\", \"continuous\" : true, \"interim_results\": true}";
ws.OnOpen += (sender, e) => ws.Send(startActionjson);
// Set the WebSocket events.
string result = string.Empty;
ws.OnMessage += Ws_OnMessage;
ws.OnError += (sender, e) =>
nf.Notify(
new NotificationMessage
{
Summary = "WebSocket Error",
Body = e.Message,
Icon = "notification-message-im"
});
ws.OnClose += (sender, e) =>
nf.Notify(
new NotificationMessage
{
Summary = String.Format("WebSocket Close ({0})", e.Code),
Body = e.Reason,
Icon = "notification-message-im"
});
ws.Connect();
//ws.SendAsync(startActionjson, b =>
//{
// if (b == true)
// {
// //send the audio as binary
// string filePath = "E:\\test33.wav";
// byte[] bytes = System.IO.File.ReadAllBytes(filePath);
// ws.SendAsync(bytes, b1 =>
// {
// if (b1)
// ws.Close();
// });
// // result+=result+ws.
// }
//});
// Connect to the server asynchronously.
//ws.ConnectAsync ();
//Console.WriteLine("\nType 'exit' to exit.\n");
string filePath = "E:\\Test3.wav";
byte[] bytes = System.IO.File.ReadAllBytes(filePath);
while (true)
{
Thread.Sleep(3000);
ws.SendAsync(bytes, b1 =>
{
if (b1)
ws.Close();
});
}
//for (int i = 0; i < bytes.Length; i += 1000000)
//{
// Thread.Sleep(1000);
// byte[] buffer = new byte[1000000];
// Buffer.BlockCopy(bytes, i, buffer, 0, 128);
// // ws.Send(buffer);
// ws.SendAsync(buffer, b1 =>
// {
// if (b1)
// ws.Close();
// });
//}
}
}
private static void Ws_OnMessage(object sender, MessageEventArgs e)
{
string s = e.Data;
}
Per the documentation, there is a 100mb limit regardless of input method. So you will have to split you audio files into chunks that are < 100mb.
To stream the audio, instead of calling System.IO.File.ReadAllBytes(filePath); and iterating over the result, I think you'll want to create a FileStream.
Also, you shouldn't immediately close the websocket once you've reached the end of file - that may prevent you from receiving all of the results. Instead, send the string {"action": "stop"} and wait until you receive a response of {"state": "listening"} which indicates that it has completed processing your audio and sent all text back.
Update: I got a hold of a windows machine, installed visual studio, and put together a working sample. I never did figure out what WebSocket API/Library you were using, but this just uses the built-in stuff that I could find documentation for on microsoft.com, so it should hopefully work for you.
I tested it with a couple of different .ogg and .wav files and confirmed that I get multiple interim and final results as expected.
using System;
using System.Net.WebSockets;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
// Perform streaming transcription of an audio file using the IBM Watson Speech to Text service over a websocket
// http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/speech-to-text.html
// https://msdn.microsoft.com/en-us/library/system.net.websockets.clientwebsocket%28v=vs.110%29.aspx
namespace WatsonSTTWebsocketExample
{
class Program
{
static void Main(string[] args)
{
Transcribe();
Console.WriteLine("Press any key to exit");
Console.ReadLine();
}
// http://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/doc/getting_started/gs-credentials.shtml
static String username = "<username>";
static String password = "<password>";
static String file = #"c:\audio.wav";
static Uri url = new Uri("wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize");
static ArraySegment<byte> openingMessage = new ArraySegment<byte>( Encoding.UTF8.GetBytes(
"{\"action\": \"start\", \"content-type\": \"audio/wav\", \"continuous\" : true, \"interim_results\": true}"
));
static ArraySegment<byte> closingMessage = new ArraySegment<byte>(Encoding.UTF8.GetBytes(
"{\"action\": \"stop\"}"
));
static void Transcribe()
{
var ws = new ClientWebSocket();
ws.Options.Credentials = new NetworkCredential(username, password);
ws.ConnectAsync(url, CancellationToken.None).Wait();
// send opening message and wait for initial delimeter
Task.WaitAll(ws.SendAsync(openingMessage, WebSocketMessageType.Text, true, CancellationToken.None), HandleResults(ws));
// send all audio and then a closing message; simltaneously print all results until delimeter is recieved
Task.WaitAll(SendAudio(ws), HandleResults(ws));
// close down the websocket
ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Close", CancellationToken.None).Wait();
}
static async Task SendAudio(ClientWebSocket ws)
{
using (FileStream fs = File.OpenRead(file))
{
byte[] b = new byte[1024];
while (fs.Read(b, 0, b.Length) > 0)
{
await ws.SendAsync(new ArraySegment<byte>(b), WebSocketMessageType.Binary, true, CancellationToken.None);
}
await ws.SendAsync(closingMessage, WebSocketMessageType.Text, true, CancellationToken.None);
}
}
// prints results until the connection closes or a delimeterMessage is recieved
static async Task HandleResults(ClientWebSocket ws)
{
var buffer = new byte[1024];
while (true)
{
var segment = new ArraySegment<byte>(buffer);
var result = await ws.ReceiveAsync(segment, CancellationToken.None);
if (result.MessageType == WebSocketMessageType.Close)
{
return;
}
int count = result.Count;
while (!result.EndOfMessage)
{
if (count >= buffer.Length)
{
await ws.CloseAsync(WebSocketCloseStatus.InvalidPayloadData, "That's too long", CancellationToken.None);
return;
}
segment = new ArraySegment<byte>(buffer, count, buffer.Length - count);
result = await ws.ReceiveAsync(segment, CancellationToken.None);
count += result.Count;
}
var message = Encoding.UTF8.GetString(buffer, 0, count);
// you'll probably want to parse the JSON into a useful object here,
// see ServiceState and IsDelimeter for a light-weight example of that.
Console.WriteLine(message);
if (IsDelimeter(message))
{
return;
}
}
}
// the watson service sends a {"state": "listening"} message at both the beginning and the *end* of the results
// this checks for that
[DataContract]
internal class ServiceState
{
[DataMember]
public string state = "";
}
static bool IsDelimeter(String json)
{
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ServiceState));
ServiceState obj = (ServiceState)ser.ReadObject(stream);
return obj.state == "listening";
}
}
}
I ran into a strange problem. When I'm trying to send the file via a TCP socket, the first 4 bytes of sended information cuts off.
That is sending and receives pieces of code.
Client side
for (var i = 0; i < fileContentByte.Length; i += buffer.Length)
{
var size = (i + buffer.Length > fileContentByte.Length) ? fileContentByte.Length - i : buffer.Length;
clientSocket.Write(fileContentByte, i, size);
}
Server side
using(var file = File.Create("C:\\test\\"+fileName.Substring(0, fileName.IndexOf('\0'))))
while(bytesReceived < numberOfBytes && (count = clientStream.Read(buffer, 0, buffer.Length)) > 0)
{
file.Write(buffer, 0, count);
bytesReceived += count;
}
Here is link on full code - http://pastebin.com/VwTgTxgb
You're doing something very strange here.
First of all, retrieval of file name can be greatly simplified down to Path.GetFileName() call.
Second, are you sure ASCII will suffice?
Third, reading the entire file into memory is OK-ish for a proof-of-concept project, but be ready to switch to streaming operations.
Fourth, your protocol is somewhat wonky. When sending variable-size payload, it is required to first tell the receiving party exactly how much bytes are you going to send. This is exactly what you don't do when sending file name.
Here's a snippet to get you started:
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace FolderSync
{
class Program
{
static void Main()
{
var server = new Server();
server.Start();
new Client().TransmitFile(
new IPEndPoint(IPAddress.Loopback, 35434),
#"f:\downloads\ubuntu-14.04.3-desktop-amd64.iso");
Console.ReadLine();
server.Stop();
}
}
class Server
{
private readonly TcpListener tcpListener;
public Server()
{
tcpListener = new TcpListener(IPAddress.Loopback, 35434);
}
public void Start()
{
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(AcceptTcpClientCallback, null);
}
public void Stop()
{
tcpListener.Stop();
}
private void AcceptTcpClientCallback(IAsyncResult asyncResult)
{
//
// Big fat warning: http://stackoverflow.com/a/1230266/60188
tcpListener.BeginAcceptTcpClient(AcceptTcpClientCallback, null);
using(var tcpClient = tcpListener.EndAcceptTcpClient(asyncResult))
using(var networkStream = tcpClient.GetStream())
using(var binaryReader = new BinaryReader(networkStream, Encoding.UTF8))
{
var fileName = binaryReader.ReadString();
var length = binaryReader.ReadInt64();
var mib = length / 1024.0 / 1024.0;
Console.WriteLine("Receiving '{0}' ({1:N1} MiB)", fileName, mib);
var stopwatch = Stopwatch.StartNew();
var fullFilePath = Path.Combine(Path.GetTempPath(), fileName);
using(var fileStream = File.Create(fullFilePath))
networkStream.CopyTo(fileStream);
var elapsed = stopwatch.Elapsed;
Console.WriteLine("Received in {0} ({1:N1} MiB/sec)",
elapsed, mib / elapsed.TotalSeconds);
}
}
}
class Client
{
public void TransmitFile(IPEndPoint endPoint, string fileFullPath)
{
if(!File.Exists(fileFullPath)) return;
using(var tcpClient = new TcpClient())
{
tcpClient.Connect(endPoint);
using(var networkStream = tcpClient.GetStream())
using(var binaryWriter = new BinaryWriter(networkStream, Encoding.UTF8))
{
var fileName = Path.GetFileName(fileFullPath);
Debug.Assert(fileName != null, "fileName != null");
//
// BinaryWriter.Write(string) does length-prefixing automatically
binaryWriter.Write(fileName);
using(var fileStream = File.OpenRead(fileFullPath))
{
binaryWriter.Write(fileStream.Length);
fileStream.CopyTo(networkStream);
}
}
}
}
}
}
I am doing a copy asynchronous operation on a file stream. I noticed that if an error happen during the operation, I don't receive any error exception.
Tested with a large file, in the middle of the copy operation, suddenly I close the network connection.
After some timeout the test concludes passed.
I Want to be able to capture the whatever error happen during the copy operation.
I copy below.. the code samples, just asking some help.
BR Alex
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;
namespace CopyAsync
{
[TestClass]
public class UnitTest
{
public int BufferSize = 10;
[TestMethod]
public void CopyFileAsyncSouldCopyFile()
{
BufferSize = 10;
const string source = #"..\..\UnitTest.cs";
var destination = Path.GetRandomFileName();
WriteLine($"Start...");
var task = CopyAsync(source, destination, action: (total) => WriteLine($"Copying... {total}"));
var bytes = task.Result;
WriteLine($"Bytes copied... {bytes}");
IsTrue(File.Exists(destination));
AreEqual((new FileInfo(source)).Length, bytes);
File.Delete(destination);
}
[TestMethod]
public void CopyFileAsyncCancelledSouldCancelCopyFile()
{
BufferSize = 10;
const string source = #"..\..\UnitTest.cs";
var destination = Path.GetRandomFileName();
var cts = new CancellationTokenSource();
WriteLine($"Start...");
var task = CopyAsync(source, destination, cts.Token,
(total) =>
{
WriteLine($"Copying... {total}");
if (total > 1677)
return;
cts.Cancel();
WriteLine($"Canceled...");
});
try
{
var bytes = task.Result; // exception WILL BE thrown here
WriteLine($"Bytes copied... {bytes}"); // WON'T BE executed
}
catch (AggregateException ex) when (ex.InnerException.GetType() == typeof(TaskCanceledException))
{
WriteLine($"TaskCanceledException...");
File.Delete(destination);
}
}
[TestMethod]
// Exception not captured
// missed: System.AggregateException: One or more errors occurred. ---> System.IO.IOException: The network path was not found.
public void CopyFileAsyncNetworkErrorShouldFail()
{
const string source = #"..\..\verybigfile.iso";
var destination = Path.GetRandomFileName();
BufferSize = 4096;
WriteLine($"Start...");
var task = CopyAsync(source, destination, action: (total) => WriteLine($"Copying... {total}"));
var bytes = task.Result; // exception WON'T BE thrown here
WriteLine($"Bytes copied... {bytes}"); // WILL BE executed
}
public async Task<int> CopyAsync(string input, string output, CancellationToken token = default(CancellationToken), Action<long> action = null)
{
using (var source = new FileStream(input, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, true))
using (var destination = new FileStream(output, FileMode.Create, FileAccess.Write, FileShare.None, BufferSize, true))
{
int bytes;
var total = 0;
var buffer = new byte[BufferSize];
while ((bytes = await source.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
{
await destination.WriteAsync(buffer, 0, bytes, token);
total += bytes;
action?.Invoke(total);
}
return total;
}
}
}
}
Here I changed a while the code, but here is the working code..
(but indeed I can't figure why now is working, since is more or less the same workflow)
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;
namespace CopyAsync
{
[TestClass]
public class UnitTest
{
private int _bufferSize = 4096;
[TestMethod]
public void CopyFileAsyncSouldCopyFile()
{
_bufferSize = 100;
const string source = #"..\..\UnitTest.cs";
var destination = Path.GetRandomFileName();
WriteLine($"Start...");
var task = FileCopyAsync(source, destination, action: total => WriteLine($"Copying... {total}"));
var bytes = task.Result;
WriteLine($"Bytes copied... {bytes}");
IsTrue(File.Exists(destination));
AreEqual((new FileInfo(source)).Length, bytes);
File.Delete(destination);
}
[TestMethod]
public void CopyFileAsyncCancelledSouldCancelCopyFile()
{
_bufferSize = 100;
const string source = #"..\..\UnitTest.cs";
var destination = Path.GetRandomFileName();
var cts = new CancellationTokenSource();
WriteLine($"Start...");
var task = FileCopyAsync(source, destination,
token: cts.Token,
action: total =>
{
WriteLine($"Copying... {total}");
if (total < 2000)
return;
cts.Cancel();
WriteLine($"Canceled... at {total}");
});
try
{
task.Wait(); // exception WILL BE thrown here... PERFECT!!!!
}
catch (AggregateException ex) when (ex.InnerException.GetType() == typeof(TaskCanceledException))
{
WriteLine($"TaskCanceledException...");
File.Delete(destination);
}
}
[TestMethod]
public void CopyFileAsyncNetworkErrorShouldFail()
{
_bufferSize = 4096;
const string source = #"\\server\sharedfolder\bigfile.iso"; // to test close network connection while copying...
var destination = Path.GetRandomFileName();
WriteLine($"Start...");
var task = FileCopyAsync(source, destination, action: total => WriteLine($"Copying... {total}"));
try
{
task.Wait(); // exception WILL BE thrown here... PERFECT!!!! more than PERFECT
}
catch (AggregateException ex) when (ex.InnerException.GetType() == typeof(IOException))
{
WriteLine($"IOException...");
File.Delete(destination);
}
}
// ##########################
public async Task<int> FileCopyAsync(string sourceFileName, string destFileName, bool overwrite = false, CancellationToken token = default(CancellationToken), Action<long> action = null)
{
if (string.Equals(sourceFileName, destFileName, StringComparison.InvariantCultureIgnoreCase))
throw new IOException($"Source {sourceFileName} and destination {destFileName} are the same");
using (var sourceStream = new FileStream(sourceFileName, FileMode.Open, FileAccess.Read, FileShare.Read, _bufferSize, true))
using (var destStream = new FileStream(destFileName, FileMode.Create, FileAccess.Write, FileShare.None, _bufferSize, true))
{
var bytesCopied = await StreamCopyAsync(sourceStream, destStream, token, action);
if (bytesCopied != (new FileInfo(sourceFileName)).Length)
throw new IOException($"Source {sourceFileName} and destination {destFileName} don't match");
return bytesCopied;
}
}
public async Task<int> StreamCopyAsync(Stream sourceStream, Stream destStream, CancellationToken token = default(CancellationToken), Action<long> action = null)
{
if (Equals(sourceStream, destStream))
throw new ApplicationException("Source and destination are the same");
using (var reg = token.Register(() => Close(sourceStream, destStream))) // disposes registration for token cancellation callback
{
int bytes;
var bytesCopied = 0;
var buffer = new byte[_bufferSize];
while ((bytes = await sourceStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
{
if (token.IsCancellationRequested)
break;
await destStream.WriteAsync(buffer, 0, bytes, token);
bytesCopied += bytes;
action?.Invoke(bytesCopied);
}
return bytesCopied;
}
}
private static void Close(Stream source, Stream destination) // fires on token cancellation
{
source.Close();
destination.Close();
}
}
}