流式CopyAsync和WriteAsync网络故障

本文关键字:网络 故障 WriteAsync CopyAsync 流式 | 更新日期: 2023-09-27 18:20:30

我正在对文件流执行复制异步操作。我注意到,如果在操作过程中发生错误,我不会收到任何错误异常。

用一个大文件测试,在复制操作的中间,我突然关闭了网络连接。

经过一段时间后,测试结果通过。

我希望能够捕捉复制操作过程中发生的任何错误。

我在下面复印。。代码示例,只是请求一些帮助。

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;
            }
        }
    }
}

流式CopyAsync和WriteAsync网络故障

这里我更改了一段时间的代码,但这里是工作代码。。

(但事实上,我不明白为什么现在有效,因为这或多或少是相同的工作流程)

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();
        }
    }
}