2012-11-23 13 views
6

Mam Web API ASP.Net skonfigurowany na mojej stronie internetowej, który jest używany do komunikacji z aplikacją pulpitu WPF. Mam konfigurację działania w interfejsie API, aby odbierać pliki binarne z aplikacji klienckiej. Jednak w niektórych (pozornie przypadkowych) przypadkach, gdy otrzymam wszystkie bajty z żądania, nie wszystkie bajty są odczytywane. Mam nadzieję, że możesz dać mi pomysł, jak to zrobić w sposób, który będzie działał przez cały czas. Oto kod:ASP.Net Web API nie odczytuje wszystkich bajtów z StreamContent

Side Klient:

public static SubmitTurnResult SubmitTurn(int turnId, Stream fileStream) 
{ 
    HttpClient client = CreateHttpClient(); 

    HttpContent content = new StreamContent(fileStream); 
    content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); 
    content.Headers.ContentDisposition.FileName = "new-turn.Civ5Save"; 
    content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 
    content.Headers.ContentLength = fileStream.Length; 

    HttpResponseMessage response = client.PostAsync(
     string.Format("SubmitTurn?authKey={0}&turnId={1}", 
         LocalSettings.Instance.AuthenticationKey, 
         turnId 
         ), 
     content 
    ).Result; 

    response.EnsureSuccessStatusCode(); 

    return response.Content.ReadAsAsync<SubmitTurnResult>().Result; 
} 

SubmitTurnResult jest enum, które definiuje wynik na serwerze, turnId jest identyfikatorem dla podmiotu plik ten jest dołączony do, i fileStream to rzeczywisty plik FileStream odczytujący liczbę bajtów dysku.

Server Side:

[HttpGet, HttpPost] 
public SubmitTurnResult SubmitTurn(string authKey, int turnId) 
{ 

    try 
    { 
     bool worked = false; 
     int gameId = 0; 

     using (GmrEntities gmrDb = new GmrEntities()) 
     { 
      var player = gmrDb.Users.FirstOrDefault(u => u.AuthKey == authKey); 
      if (player != null) 
      { 
       var turn = player.Turns.FirstOrDefault(t => t.TurnID == turnId); 
       if (turn != null) 
       { 
        byte[] saveFileBytes = null; 

        using (MemoryStream tempStream = new MemoryStream()) 
        { 
         var task = this.Request.Content.CopyToAsync(tempStream); 
         task.Wait(); 

         saveFileBytes = tempStream.ToArray(); 
         tempStream.Close(); 
        } 

        if (saveFileBytes.Length != this.Request.Content.Headers.ContentLength.Value) 
        { 
         throw new Exception(string.Format("Byte array length ({0}) not equal to HTTP content-length header ({1}). This is not good!", 
            saveFileBytes.Length, this.Request.Content.Headers.ContentLength.Value)); 
        } 

        worked = GameManager.SubmitTurn(turn, saveFileBytes, gmrDb); 

        if (worked) 
        { 
         gameId = turn.Game.GameID; 

         gmrDb.SaveChanges(); 
        } 
       } 
      } 
     } 


     return SubmitTurnResult.OK; 
    } 
    catch (Exception exc) 
    { 
     DebugLogger.WriteExceptionWithComments(exc, string.Format("Diplomacy: Sumbitting turn for turnId: {0}", turnId)); 

     return SubmitTurnResult.UnexpectedError; 
    } 
} 
+0

Czy był to 32-bitowy system Windows Server 2003 lub Windows XP? Zachowujemy się tak samo z StreamContent, ale podczas przesyłania strumieniowego odpowiedzi z usługi Web API systemu Windows Server 2003. Nie ma repro w 2008 roku. Odkryliśmy również, że jest to tylko repros z FileStream. Konwersja strumienia FileStream do MemoryStream omija problem (oczywiście kosztem pamięci). Odkryliśmy, że kiedy strumień odpowiedzi kończy się wcześniej, zawsze jest na granicy 4096 bajtów i osiąga limit około 3,5 MB. –

Odpowiedz

11

Jak wspomniano w poprzednim komentarzu, wpadliśmy w tym samym zachowanie z StreamContent, ale podczas przesyłania strumieniowego odpowiedź z serwera 2003 Service Web API Windows. To nie repro w 2008 roku. W rzeczywistości, to również repros na Windows Server 2008, jeśli skonfiguruję maszynę wirtualną z małą ilością pamięci RAM (712 MB), ale z 4 GB pamięci RAM nie repro. Odkryliśmy również, że jest to tylko repros z FileStream. Konwersja strumienia FileStream do MemoryStream omija problem (oczywiście kosztem pamięci). Stwierdziliśmy, że kiedy strumień odpowiedzi kończy się wcześniej, zawsze jest na granicy 4096 bajtów i osiąga pułap na poziomie około 3,5 MB.

Oto obejście ustaloną rzeczy dla mnie, dostosowane do przykładu kodu:

public static SubmitTurnResult SubmitTurn(int turnId, Stream fileStream) 
{ 
    HttpClient client = CreateHttpClient(); 

    var memoryStream = new MemoryStream((int)fileStream.Length); 
    fileStream.CopyTo(memoryStream); 
    fileStream.Close(); 
    memoryStream.Seek(0, SeekOrigin.Begin); 
    HttpContent content = new StreamContent(memoryStream); 

W razie potrzeby, można warunkowo wykonaj MemoryStream skopiować tylko gdy Stream jest FileStream.

+0

Dzięki za sugestię, niestety to nie rozwiązało problemu. Część kliencka kodu działa na setkach maszyn od WinXP do Win8.1 i zmieniłem go tak, aby używał strumienia MemoryStream zamiast FileStream. Część serwerowa działa na WinServer 2012R2. Nadal doświadczam tego problemu od czasu do czasu, gdy ilość bajtów odczytanych ze strumienia treści przez serwer nie zgadza się z ilością bajtów wysłanych przez klienta = \ –

+0

To naprawdę niefortunne i nie wróży dobrze mojego "rozwiązania" "Wydaje mi się, że to działa jak na razie. Najlepsze, co można zrobić w twoim przypadku, to zbudować logikę ponownych prób po stronie klienta, jeśli odpowiedź HTTP nie powiedzie się. Lub zgłoś zgłoszenie błędu w firmie Microsoft, najlepiej w przypadku testowym, które może zafałszować problem, prawdopodobnie przez wielokrotne uruchamianie w pętli. Jeśli masz coś, co odsyłasz, możesz skorzystać z pomocy technicznej firmy Microsoft, dać im swoją kartę kredytową i nie będą pobierać opłaty, jeśli potwierdzą, że to naprawdę błąd po ich zakończeniu. To czasochłonne, ale działa. Powodzenia! –