2011-12-20 8 views
16

La nostra applicazione Web (Web Form ASP.NET) ha una pagina che visualizza un file PDF generato di recente per gli utenti. Poiché il file PDF è talvolta abbastanza grande, abbiamo implementato un approccio "streaming" per inviarlo al browser client in blocchi.Perché ASP.NET sostituisce un'intestazione Content-Length con un'intestazione Transfer-Encoding quando si svuota manualmente una risposta?

Nonostante l'invio dei dati in blocchi, conosciamo l'intera dimensione del file prima di inviarlo, quindi impostiamo l'intestazione Content-Length in modo appropriato. Questo ha funzionato nel nostro ambiente di produzione per un po '(e continua a funzionare nel nostro ambiente di test con una configurazione praticamente identica) fino ad oggi. Il problema segnalato era che Chrome avrebbe tentato di aprire il file PDF ma si sarebbe bloccato con l'animazione "Caricamento" bloccata.

Poiché tutto funzionava ancora bene nel nostro ambiente di test, ero in grado di usare Firebug per dare un'occhiata alle intestazioni di risposta che tornavano in entrambi gli ambienti. Nell'ambiente di test, stavo vedendo un'intestazione appropriata "Content-Length", mentre in produzione era stata sostituita con una codifica di trasferimento : chunked. A Chrome non piace questo, quindi il riagganciarsi.

Ho letto alcuni articoli e post che parlano di come può essere visualizzata l'intestazione Transfer-Encoding quando non viene fornita l'intestazione Content-Length, ma stiamo specificando l'intestazione Content-Length e tutto sembra funzionare correttamente durante l'esecuzione del stesso codice per lo stesso file PDF su un server di prova.

Sia i server di test che di produzione eseguono IIS 7.5 ed entrambi hanno la compressione dinamica e statica abilitata.

Ecco il codice in questione:

var fileInfo = new FileInfo(fileToSendDown); 
Response.ClearHeaders(); 
Response.ContentType = "application/pdf";    
Response.AddHeader("Content-Disposition", "filename=test.pdf"); 
Response.AddHeader("Content-Length", fileInfo.Length.ToString()); 
var buffer = new byte[1024]; 
using (var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read)) 
{ 
    int read; 
    while ((read = fs.Read(buffer, 0, 1024)) > 0) 
    { 
     if (!response.IsClientConnected) break; 
     Response.OutputStream.Write(buffer, 0, read); 
     Response.Flush(); 
    } 
} 

ho avuto la fortuna di vedere lo stesso comportamento sulla mia workstation locale in modo da utilizzare il debugger ho potuto vedere che il 'Transfer-Encoding: chunked' intestazione è impostato sul 2 ° passaggio del ciclo while durante la chiamata a "Flush". A quel punto, la risposta ha sia un'intestazione Content-Length che un'intestazione Transfer-Encoding, ma in qualche modo quando la risposta raggiunge il browser Firebug mostra solo l'intestazione Transfer-Encoding.

UPDATE

Penso di aver rintracciato questo fino a utilizzando una combinazione di invio dei dati verso il basso in "blocchi" e allegando un 'filtro' per l'oggetto HttpResponse (stavamo usando un filtro per tenere traccia la dimensione di viewstate inviata a ogni pagina). Non ha senso usare noi un filtro HTTP quando si invia un PDF al browser, quindi cancellare il filtro qui ha risolto il nostro problema. Ho deciso di scavare un po 'più a fondo per pura curiosità e ho aggiornato questa domanda se qualcun altro dovesse mai imbattersi in questo problema in futuro.

Ho una semplice app su AppHarbor che riproduce il problema: http://transferencodingtest.apphb.com/. Se controlli sia "Usa filtro?" e "Send In Chunks?" i riquadri dovrebbero essere in grado di visualizzare l'intestazione 'transfer-encoding: chunked' (utilizzando gli strumenti di sviluppo di Chrome, Firebug, Fiddler, qualsiasi altra cosa). Se una delle caselle non è selezionata, otterrai un'intestazione corretta per la lunghezza del contenuto. Il codice sottostante è su GitHub modo da poter vedere cosa sta succedendo dietro le quinte:

https://github.com/appakz/TransferEncodingTest

Si noti che a Repro localmente avresti bisogno di configurare un sito web locale in IIS 7.5 (7 può anche funzionare, Non ho provato). Il server di sviluppo ASP .NET fornito con Visual Studio NON restituisce il problema.

ho aggiunto qualche dettaglio in più di un post sul blog qui: 'Content-Length' Header Replaced With 'Transfer-Encoding: Chunked' in ASP .NET

+0

So che questa è una vecchia domanda, ma facendo ricerche simili mi sono imbattuto in [questo eccellente post] (http://geekswithblogs.net/GruffCode/archive/2012/01/02/lsquocontent-lengthrsquo-header-replaced-with-lsquotransfer-encoding-chunkedrsquo-in-asp- .net.aspx) spiegando questo stesso problema. Ci vuole una serie specifica di circostanze per creare questo problema. Spero che questo aiuti un futuro lettore. – user158017

+1

Questo è il post che ho scritto dopo aver fatto la domanda, non ricevendo una risposta, e infine ho trovato alcuni passaggi per riprodurlo in modo coerente. Ho aggiornato la domanda originale con i link all'app di esempio che la riproduce, ma suppongo che dovrei aggiungere anche un link al post del blog. –

risposta

4

Da an article on MSDN sembra che è possibile disattivare la codifica Chunked:

appcmd set config /section:asp /enableChunkedEncoding:False 

enter image description here

ma è menzionato sotto ASP settings , quindi potrebbe non essere applicabile a una risposta generata da un gestore ASP.NET.

+1

Avevo già esaminato queste impostazioni, ma non hanno avuto alcun impatto sul problema. –

3

Una volta chiamato Response.Flush(), il corpo della risposta è in fase di invio al client, quindi non è possibile aggiungere ulteriori intestazioni alla risposta. Trovo molto improbabile che una seconda chiamata a Response.Flush() stia aggiungendo l'intestazione Transfer-Encoding in quel momento.

Si dice di avere abilitato la compressione. Ciò richiede quasi sempre una risposta frammentaria. Quindi sarebbe logico che se il server conosce lo Content-Length prima della compressione, potrebbe sostituire quell'intestazione per l'intestazione Transfer-Encoding e ridurre la risposta. Tuttavia, anche con la compressione abilitata sul server, il client deve dichiarare esplicitamente il supporto per la compressione nell'intestazione della richiesta Accept-Encoding oppure il server non può comprimere la risposta. Hai controllato per questo nei tuoi test?

In una nota finale, poiché si sta chiamando Response.Flush() manualmente, provare a impostare Response.Buffer = True e Response.BufferOutput = False. Apparentemente hanno effetti contrastanti su come funziona Response.Flush(). Vedere il commento nella parte inferiore di this page e this page.

+0

Sono d'accordo che non ha alcun senso che le intestazioni "cambino" sulla seconda chiamata a Flush, ma ho provato questo stesso codice con e senza inviare i dati in blocchi più piccoli, e il browser registra solo dopo aver ricevuto l'intestazione "Transfer-Encoding" quando vengono utilizzate più chiamate per il flush. Sto lavorando ad una piccola app di esempio che riproporrà e pubblicherà quando l'avrò. –

0

Ho avuto un problema simile quando stavo scrivendo un CSV di grandi dimensioni (il file non esisteva scrivo una stringa riga per riga iterando attraverso una raccolta in memoria e generando la linea) chiamando Response.Write sullo stream di risposta con BufferOutput impostata su false, ma la soluzione era quella di cambiare

Reponse.ContentType = 'text/csv'-Reponse.ContentType = 'application/octet-stream'

Quando il tipo di contenuto non è stato impostato a sono stati aggiunti application/octet-stream un mucchio di altre intestazioni di risposta, come Content-Encoding - gzip

Problemi correlati