对网络故障流CopyAsync和WriteAsync

问题描述:

我正在做一个文件流的副本异步操作。我注意到,如果在操作过程中发生错误,我不会收到任何错误异常。对网络故障流CopyAsync和WriteAsync

用大文件测试,在复制操作过程中,突然关闭网络连接。

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

我希望能够捕获复制操作期间发生的任何错误。

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

BR亚历

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

    } 
} 
+0

哪边是网络,读或写? –

+0

读取端类似于\\ server \ sharedFolder \ verybigfile.iso, – alhpe

+0

最后一个测试是从UNC路径(如\\ server \ sharedfolder \ verybigfile.iso)对硬盘进行应对 如果在复制过程中将网络电缆从桌面,而不是抛出错误测试通过。我会喜欢有系统抛出异常 但是,如果您从E:\ verybigfile.iso等可移动设备上复制,并在复制时删除笔式驱动器,然后是,则系统会引发异常:System.AggregateException:发生一个或多个错误。 ---> System.IO.FileNotFoundException:无法找到指定的文件。这对我来说行为很好 – alhpe

在这里,我改变了,而代码,但这里是工作的代码..

(但我确实想不通为什么现在是工作,因为是多了还是少一样的工作流程)

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

我从来不喜欢像'ex.InnerException.GetType()== typeof(IOException)'这样的事情,因为使用派生类可以破坏它,通常我使用'ex.InnerException测试是IOException'测试 –

+0

好点谢谢! – alhpe