2016-04-22 11 views
0

Ich habe eine Desktop-App, die 1 oder mehrere kleine Dateien (jpg mit weniger als 400 KB Größe und nicht mehr als 20 gleichzeitig) gleichzeitig mit einem CustomWebClient-Objekt herunterlädt und ruft OpenReadAsync() auf. Der Download-Prozess funktioniert problemlos, wenn kein Problem dabei auftritt. Ich möchte die Antwort auf eine bestimmte Zeit (15 Sekunden) begrenzen, also habe ich eine timeOut-Behandlung eingeführt, die die Anfrage abbricht. Sogar das Timeout funktioniert und danach empfängt meine "OpenReadCompletedEventHandler" -Methode System.Net.WebException: Die Anfrage wurde abgebrochen: Die Anfrage wurde abgebrochen (was das richtige Verhalten ist). Nun ist mein Problem, dass ich dem Benutzer erlauben möchte, das Bild (die Bilder) erneut zu laden. Die nächste WebClient-Anforderung schlägt also mit derselben WebException fehl. Unten ist mein Code.Zweite WebClient-Anfrage kann nach Zeitüberschreitung und Abbruch der Anfrage nicht ausgeführt werden

Hier ist meine Gewohnheit WebClient-Klasse (verwendet, um zu einem Zeitpunkt mehr als 2 Asynchron-Verbindungen zu haben):

internal class ExtendedWebClient : WebClient 
{ 
    private Timer _timer; 
    public int ConnectionLimit { get; set; } 
    public int ConnectionTimeout { get; set; } 
    public ExtendedWebClient() 
    { 
     this.ConnectionLimit = 2; 
    } 

    protected override WebRequest GetWebRequest(Uri address) 
    { 
     var request = base.GetWebRequest(address) as HttpWebRequest; 

     if (request != null){_timer = new Timer(TimeoutRequest, request, ConnectionTimeout, Timeout.Infinite); 

      request.ServicePoint.ConnectionLimit = this.ConnectionLimit; 
      request.ServicePoint.MaxIdleTime = 5000; 
      request.ServicePoint.ConnectionLeaseTimeout = 5000; 
     } 

     return request; 
    } 

    private void TimeoutRequest(object state) 
    { 
     _timer.Dispose(); 
     _timer = null; 
     ((WebRequest)state).Abort(); 
    } 

    protected override void Dispose(bool disposing) 
    { 
     if (_timer != null) 
     { 
      _timer.Dispose(); 
      _timer = null; 
     } 
     base.Dispose(disposing); 
    }  
} 

Hier ist der Code, die Dateien mit meiner benutzerdefinierten WebClient-Klasse zum Download:

internal struct PageWaitHandleState 
{ 
    public int WaitHandleIndexInPage; 
    public bool ImageIsLoaded; 
    public string ErrMessage; 
} 

public Image[] downloadedImages; 

private PageWaitHandleState[] waitHandlesInPage; 
private OpenReadCompletedEventHandler[] downloadComplete; 
private EventWaitHandle[] pagesEWH = null; 
private EventWaitHandle[] downloadImageEvent; 
private int availableImages = 1; // Set here to simplify, but as I stated in my description, it may be more than 1. 
int downloadTimeOut = 15000; 
int maxSimultaneousDownloads = 20; 

private void DownloadImages(int pageIndex = 0, string[] imageUrl) 
{ 
    if (pagesEWH[pageIndex] != null) 
    { 
     ReloadImages(pageIndex, imageUrl); // Executed in the second request 
     return; 
    else 
    { 
     pagesEWH[pageIndex] = new EventWaitHandle[availableImages]; 
     downloadedImages = new Image[availableImages]; 
     downloadComplete = new OpenReadCompletedEventHandler[availableImages]; 
     downloadImageEvent = new EventWaitHandle[availableImages]; 
     waitHandlesInPage = new PageWaitHandleState[availableImages]; 

     // Set the downloadComplete deletages 
     for (int i = 0; i < availableImages; i++) 
     { 
      downloadComplete[i] = ProcessImage; 
     } 
    } 

    for (int imgCounter = 0; i < availableImages; i++) 
    { 
     waitHandlesInPage[imgCounter] = new PageWaitHandleState() { ImageIsLoaded = false, WaitHandleIndexInPage = imgCounter, ErrMessage = null }; 
     downloadImageEvent[imgCounter] = GrabImageAsync(imageUrl[imgCounter], downloadComplete[imgCounter], imgCounter, downloadTimeOut, maxSimultaneousDownloads); 
     pagesEWH[imgCounter] = downloadImageEvent[imgCounter]; 
    } 

     offenderIndex++; 
    } 
} 

private static EventWaitHandle GrabImageAsync(string url, OpenReadCompletedEventHandler openReadCompletedEventHandler, int imgCounter, int downloadTimeOut, int maxSimultaneousDownloads) 
{ 
    var myClient = new ExtendedWebClient(); 
    myClient.ConnectionLimit = maxSimultaneousDownloads; 
    myClient.ConnectionTimeout = downloadTimeOut; 
    myClient.OpenReadCompleted += openReadCompletedEventHandler; 
    var iewh = new ImageEventWaitHandle() { ewh = new EventWaitHandle(false, EventResetMode.ManualReset), ImageIndex = imgCounter }; 
    myClient.OpenReadAsync(new Uri(url), iewh); 
    return iewh.ewh; 
} 

internal void ProcessImage(object sender, OpenReadCompletedEventArgs e) 
{ 
    ImageEventWaitHandle iewh = (ImageEventWaitHandle)e.UserState; 
    bool disposeObject = false; 

    try 
    { 
     if (e.Cancelled) 
     { 
      this.waitHandlesInPage[iewh.ImageIndex].ImageIsLoaded = false; 
      this.waitHandlesInPage[iewh.ImageIndex].ErrMessage = "WebClient request was cancelled"; 
     } 
     else if (e.Error != null) 
     { 
      this.waitHandlesInPage[iewh.ImageIndex].ImageIsLoaded = false; 
      this.waitHandlesInPage[iewh.ImageIndex].ErrMessage = e.Error.Message; 
      iewh.ewh.Set(); 
      this.downloadImageEvent[iewh.ImageIndex].Close(); 
     } 
     else 
     { 
      using (Stream inputStream = e.Result) 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       byte[] buffer = new byte[4096]; 
       int bytesRead; 
       int totalReadBytes = 0; 
       do 
       { 
        bytesRead = inputStream.Read(buffer, 0, buffer.Length); // Exception fired here with the second request 
        ms.Write(buffer, 0, bytesRead); 
        totalReadBytes += bytesRead; 
       } while (inputStream.CanRead && bytesRead > 0); 

       this.downloadedImages[iewh.ImageIndex] = Image.FromStream(ms); 
       this.waitHandlesInPage[iewh.ImageIndex].ImageIsLoaded = true; 
       this.waitHandlesInPage[iewh.ImageIndex].ErrMessage = null; 
      } 
      disposeObject = true; 
     } 
    } 
    catch (Exception exc) 
    { 
     this.downloadedImages[iewh.ImageIndex] = null; 
    } 
    finally 
    { 
     // Signal the wait handle 
     if (disposeObject) 
     { 
      iewh.ewh.Set(); 
      ((WebClient)sender).Dispose(); 
     } 
    } 
} 

private void ReloadImages(int pageIndex, string[] imageUrl) 
{ 
    for (int imgCounter = 0; imgCounter < availableImages; imgCounter++) 
    { 
     this.downloadComplete[imgCounter] = this.ProcessImage; 
     this.waitHandlesInPage[imgCounter] = new PageWaitHandleState() { ImageIsLoaded = false, WaitHandleIndexInPage = imgCounter, ErrMessage = null }; 
     this.downloadImageEvent[imgCounter] = GrabImageAsync(ImageUrl[imgCounter],this.downloadComplete[imgCounter], imgCounter, downloadTimeOut, maxSimultaneousDownloads); 
     this.pagesEWH[imgCounter] = this.downloadImageEvent[imgCounter]; 
    } 
} 

Schließlich, wenn ich die Bilder zugreifen möchte ich überprüfen, ob sie bereit sind, unter Verwendung von:

private bool ImagesInPageReady(int pageIndex, int recordsInCurrentPage) 
{ 
    if (_PagesEWH[pageIndex] != null) 
    { 
     int completedDownloadsCount = 0; 
     bool waitHandleSet; 

     // Wait for the default images first (imgCounter = 0). When moving page or asking for more pictures, then wait for the others. 
     for (int ewhIndexInPage = 0; ewhIndexInPage < recordsInCurrentPage; ewhIndexInPage++) 
     { 
      if (this.pagesEWH[ewhIndexInPage].WaitOne(this.downloadTimeOut)) 
      { 
       if (this.WaitHandlesInPage[ewhIndexInPage].ImageIsLoaded) 
       { 
        completedDownloadsCount++; 
       } 
      } 
      else 
      { 
       this.pagesEWH[ewhIndexInPage].Set(); 
      } 
     } 

     return (completedDownloadsCount > 0); 
    } 

    return false; 
} 
+0

Dies ist für die Frage unerheblich, aber viele APIs sind hier veraltet. Sie können den Code wahrscheinlich mit TPL und 'HttpClient' und' await' um die Hälfte verkleinern. – usr

+0

Ich benutze .NET 4, so kann ich leider nicht verwenden warten. Danke für deinen Kommentar. –

+0

Sie können erwarten mit 4.0. – usr

Antwort

0

@usr, danke, dass du mich in die richtige Richtung weist. HttpClient war die Lösung. Also verkapselte ich mein HttpClient-Objekt in einer neuen Klasse zusammen mit der ProcessImage() -Methode und dem Exposing und dem Ereignis, die von der gleichen Methode ausgelöst wurden.