2012-11-14 14 views
11

Sto cercando di utilizzare HttpListener per servire file statici, e questo funziona bene con file di piccole dimensioni. Quando le dimensioni dei file si sviluppano più grandi (testato con 350 e file 600MB), il server soffoca con una delle seguenti eccezioni:

HttpListenerException: L'operazione di I/O è stata interrotta a causa di un exit di thread o una richiesta di applicazione, o:
HttpListenerException: Il periodo di timeout del semaforo scaduto.Serve file di grandi dimensioni con C# HttpListener

Che cosa deve essere modificato per eliminare le eccezioni e farlo funzionare stabile/affidabile (e veloce)?

Ecco alcune ulteriori elaborazioni: Questa è fondamentalmente una domanda successiva a this earlier question. Il codice è leggermente esteso per mostrare l'effetto. La scrittura del contenuto è in loop con dimensioni del chunk (si spera ragionevoli), 64kB nel mio caso, ma la modifica del valore non ha fatto la differenza tranne la velocità (vedere la precedente domanda citata).


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

sto cercando il download in un browser e anche in un programma C# utilizzando HttpWebRequest, non fa alcuna differenza.

Sulla base delle mie ricerche, suppongo che HttpListener non sia realmente in grado di svuotare il contenuto sul client o almeno lo faccia al proprio ritmo. Ho anche lasciato fuori il BinaryWriter e ho scritto direttamente sullo stream: nessuna differenza. Introdotto un BufferedStream attorno al flusso di base: nessuna differenza. Abbastanza divertente, se un thread.Sleep (200) o leggermente più grande viene introdotto nel ciclo, funziona sulla mia scatola. Tuttavia dubito che sia abbastanza stabile per una soluzione reale. This question dà l'impressione che non ci sia alcuna possibilità di farlo funzionare correttamente (oltre a spostarsi su IIS/ASP.NET a cui farei ricorso, ma più probabilmente starne lontano se possibile).

risposta

10

Non ci hai mostrato l'altra parte critica come hai inizializzato HttpListener. Quindi ho provato il codice con quello sottostante e ha funzionato

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 è il vostro codice in cui Thread.Sleep(200); viene rimosso.

Se si desidera vedere il codice completo di esso.


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! Devo ammettere che mi sono imbattuto in un'ossessione che potrebbe esserci qualche problema con HttpListener sotto i cappucci, invece di pensare chiaramente. Quindi @ L.B grazie per avermi riportato sulla strada giusta. Fondamentalmente sto istanziando HttpListener nello stesso modo, e ottengo gli errori usando il tuo codice. Ovviamente l'ho provato su diversi PC nel nostro ambiente e ho visto lo stesso effetto, ma su una macchina virtuale pulita, il tuo codice e la mia implementazione originale funzionano entrambi in modo impeccabile. Devo ancora scoprire qual è la causa, sospetto AntiVirus ... Quindi grazie ancora per il tuo aiuto! – lichtalberich

+1

@ L.B .: Puoi per favore indicare qual è il vantaggio dell'utilizzo del BinaryWriter aggiuntivo? –

+0

Qual è la grande differenza qui rispetto alla domanda? Riassumi il problema e la soluzione? –

Problemi correlati