2016-02-11 11 views
6

Ho creato un controller WebAPI, basato su MVC 5, che fornisce file diversi per i nostri clienti. Lo strumento per accedere ai file è anche auto-scritto - basato su .NET HttpClient - ma questa è un'altra storia.MVC 5 WebAPI - Download file - HttpException

Nella prima versione del controller scaricare ho usato la configurazione in meccanismo per fornire i file come this

Ma questo meccanismo si è schiantato sul mio IIS per i file> 4GB.

Così ho finalmente venuto a questo codice:

public class DownloadController : ApiController 
    { 
     public async Task Get(long id) 
     { 
      string fullFilePath = GetFilePathById(id); 
      string returnFileName = fullFilePath.Split('\\').Last(); 
      FileInfo path = new FileInfo(fullFilePath); 
      HttpContext.Current.Response.ContentType = "application/zip"; 
      HttpContext.Current.Response.AddHeader("Content-Length", path.Length.ToString()); 
      HttpContext.Current.Response.AddHeader("Content-Disposition", "attachment; filename=" + returnFileName); 
      HttpContext.Current.Response.Flush(); 
     try 
     { 
      byte[] buffer = new byte[4096]; 
      using (FileStream fs = path.OpenRead()) 
      { 
       using (BufferedStream bs = new BufferedStream(fs, 524288)) 
       { 
        int count = 0; 
        while ((count = bs.Read(buffer, 0, buffer.Length)) > 0) 
        { 
         if (!HttpContext.Current.Response.IsClientConnected) 
         { 
          break; 
         } 
         HttpContext.Current.Response.OutputStream.Write(buffer, 0, count); 
         HttpContext.Current.Response.Flush(); 
        } 
       } 
      } 
     } 
     catch (Exception exception) 
     { 
      //Exception logging here 
     } 
    } 
} 

Questo codice funziona molto bene e ho avuto download veloci con l'utilizzo della CPU accettabili e del disco I/O. Ma dopo qualche tempo ho notato che - con ogni singolo file - un'eccezione non gestita scrive una voce nell'applicazione Eventlog del server IIS in questo modo:

Server cannot set status after HTTP headers have been sent 

Exception type: HttpException 

Event Log ID 1309 

Sono sicuro che l'uso ricorrente di .Flush() causa il problema, ma se rimuovo qualcuno di questi il ​​download smette di funzionare.

In domande simili posso trovare Response.BufferOutput = true; come soluzione ma sembra che mangi tutte le risorse del mio server e ritardi il download.

Qualsiasi suggerimento sarebbe fantastico!

+1

Hai provato ad aggiungere un 'HttpContext.Current.Response.Close()' alla fine? Oppure, se applicabile (e non hai bisogno di ulteriori elaborazioni), 'HttpContext.Current.ApplicationInstance.CompleteRequest()'? – Jcl

+0

Qual è il motivo per cui non si utilizza StreamContent? – martennis

+0

@Jcl Ci proverò e ti darò un feedback. – maltmann

risposta

1

Il problema non è con il Flush(), ma che non si sta chiudendo il flusso di risposta te stesso con HttpContext.Current.Response.Close();

Il framework ASP.NET non sa cosa si sta facendo all'interno del metodo di azione, quindi passa il richiesta tramite la solita pipa di richiesta, che fa tutto l'impianto idraulico necessario al posto nostro. Uno di questi è che invia le intestazioni e lo stato HTTP al client. Tuttavia, hai già impostato e inviato le intestazioni quando il framework prova a farlo. Per evitare ciò, è necessario chiudere lo stream e terminare l'elaborazione manualmente chiudendo il flusso di risposta.

+0

. Chiudere() non lo risolve. Hai idea di come posso evitare di inviare le intestazioni HTTP? – maltmann

+0

Puoi controllare in Fiddler le intestazioni che vengono inviate? – Tamas

+0

Le intestazioni arrivano solo una volta. Tendo a sopprimere questa eccezione globalmente per mantenere puliti i registri degli eventi del server – maltmann