2012-11-14 16 views
11

Ich versuche, HttpListener statische Dateien zu dienen, und das funktioniert gut mit kleinen Dateien. Wenn die Dateigröße größer wird (getestet mit 350 und 600 MB), erstickt der Server mit einer der folgenden Ausnahmen:
HttpListenerException: Der E/A-Vorgang wurde aufgrund eines Thread-Exits oder einer Anwendungsanforderung abgebrochen, oder:
HttpListenerException: Die Semaphor Timeout-Periode ist abgelaufen.Serving große Dateien mit C# HttpListener

Was muss geändert werden, um die Ausnahmen loszuwerden und sie stabil/zuverlässig (und schnell) laufen zu lassen?

Hier ist eine weitere Ausarbeitung: Dies ist im Grunde eine Follow-up-Frage zu this earlier question. Der Code ist leicht erweitert, um den Effekt zu zeigen. Inhalt schreiben ist in einer Schleife mit (hoffentlich vernünftigen) Chunk-Größen, 64kB in meinem Fall, aber die Änderung des Wertes hat keinen Unterschied außer der Geschwindigkeit (siehe die ältere Frage).


using(FileStream fs = File.OpenRead(@"C:\test\largefile.exe")) { 

    //response is HttpListenerContext.Response... 
    response.ContentLength64 = fs.Length; 
    response.SendChunked = false; 
    response.ContentType = System.Net.Mime.MediaTypeNames.Application.Octet; 
    response.AddHeader("Content-disposition", "attachment; filename=largefile.EXE"); 

    byte[] buffer = new byte[ 64 * 1024 ]; 
    int read; 
    using(BinaryWriter bw = new BinaryWriter(response.OutputStream)) { 
     while((read = fs.Read(buffer, 0, buffer.Length)) > 0) { 
      Thread.Sleep(200); //take this out and it will not run 
      bw.Write(buffer, 0, read); 
      bw.Flush(); //seems to have no effect 
     } 

     bw.Close(); 
    } 

    response.StatusCode = (int)HttpStatusCode.OK; 
    response.StatusDescription = "OK"; 
    response.OutputStream.Close(); 
} 

ich den Download in einem Browser und auch in einem C# Programm mit HttpWebRequest versuche, macht es keinen Unterschied.

Basierend auf meinen Recherchen nehme ich an, dass HttpListener nicht wirklich in der Lage ist, Inhalte zum Client zu putzen oder zumindest in seinem eigenen Tempo. Ich habe auch den BinaryWriter weggelassen und direkt in den Stream geschrieben - kein Unterschied. Einführung eines BufferedStreams um den Basisstream - kein Unterschied. Lustig genug, wenn ein Thread.Sleep (200) oder etwas größer in die Schleife eingeführt wird, funktioniert es auf meiner Box. Ich bezweifle jedoch, dass es stabil genug für eine echte Lösung ist. This question erweckt den Eindruck, dass es überhaupt keine Chance gibt, es richtig laufen zu lassen (abgesehen von der Umstellung auf IIS/ASP.NET, auf die ich zurückgreifen würde, aber wahrscheinlich nicht, wenn möglich).

Antwort

10

Sie haben uns den anderen kritischen Teil nicht gezeigt, wie Sie HttpListener initialisiert haben. Deshalb habe ich versucht, den Code mit dem unten und es funktionierte

HttpListener listener = new HttpListener(); 
listener.Prefixes.Add("http://*:8080/"); 
listener.Start(); 
Task.Factory.StartNew(() => 
{ 
    while (true) 
    { 
     HttpListenerContext context = listener.GetContext(); 
     Task.Factory.StartNew((ctx) => 
     { 
      WriteFile((HttpListenerContext)ctx, @"C:\LargeFile.zip"); 
     }, context,TaskCreationOptions.LongRunning); 
    } 
},TaskCreationOptions.LongRunning); 

WriteFile ist der Code, wo Thread.Sleep(200); entfernt wird.

Wenn Sie den vollständigen Code davon sehen möchten.


void WriteFile(HttpListenerContext ctx, string path) 
{ 
    var response = ctx.Response; 
    using (FileStream fs = File.OpenRead(path)) 
    { 
     string filename = Path.GetFileName(path); 
     //response is HttpListenerContext.Response... 
     response.ContentLength64 = fs.Length; 
     response.SendChunked = false; 
     response.ContentType = System.Net.Mime.MediaTypeNames.Application.Octet; 
     response.AddHeader("Content-disposition", "attachment; filename=" + filename); 

     byte[] buffer = new byte[64 * 1024]; 
     int read; 
     using (BinaryWriter bw = new BinaryWriter(response.OutputStream)) 
     { 
      while ((read = fs.Read(buffer, 0, buffer.Length)) > 0) 
      { 
       bw.Write(buffer, 0, read); 
       bw.Flush(); //seems to have no effect 
      } 

      bw.Close(); 
     } 

     response.StatusCode = (int)HttpStatusCode.OK; 
     response.StatusDescription = "OK"; 
     response.OutputStream.Close(); 
    } 
} 
+0

Doh! Ich muss zugeben, dass ich in eine Obsession geraten bin, dass es ein Problem mit HttpListener unter den Hauben geben könnte, anstatt nur klar zu denken. Also @ L.B danke, dass du mich wieder auf den richtigen Weg gebracht hast. Grundsätzlich instanziiere ich HttpListener auf die gleiche Weise, und ich bekomme die Fehler mit Ihrem Code. Natürlich habe ich es auf verschiedenen PCs in unserer Umgebung ausprobiert und den gleichen Effekt gesehen, aber auf einer sauberen VM-Maschine funktionieren sowohl Ihr Code als auch meine ursprüngliche Implementierung einwandfrei. Ich muss noch herausfinden, was die Ursache ist, ich vermute AntiVirus ... Also nochmals vielen Dank für Ihre Hilfe! – lichtalberich

+1

@ L.B.: Können Sie bitte angeben, welchen Vorteil der zusätzliche BinaryWriter bietet? –

+0

Was ist der große Unterschied hier verglichen mit der Frage? Können Sie das Problem und die Lösung zusammenfassen? –