2015-11-04 129 views
1

我正在做一個文件流的副本異步操作。我注意到,如果在操作過程中發生錯誤,我不會收到任何錯誤異常。對網絡故障流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

回答

0

在這裏,我改變了,而代碼,但這裏是工作的代碼..

(但我確實想不通爲什麼現在是工作,因爲是多了還是少一樣的工作流程)

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