2009-05-03 4 views
13

In questo momento sto cercando di servire file CSS e JS da un server che non mi consente di abilitare mod_gzip o mod_deflate. Così ho scritto un piccolo script PHP per comprimere con GZIP e tornare all'utente.Come determinare la lunghezza del contenuto di un file gzip?

Esempio di codice:

$filename = "style.css"; 

if (!file_exists($filename) || !($info = stat($filename))) { 
    header("HTTP/1.1 404 Not Found"); 
    die(); 
} 

header("Date: ".gmdate("D, j M Y H:i:s e", time())); 
header("Cache-Control: max-age=2592000"); 
header("Last-Modified: ".gmdate("D, j M Y H:i:s e", $info['mtime'])); 
header("Etag: ".sprintf("\"%x-%x-%x\"", $info['ino'], $info['size'], $info['mtime'])); 
header("Accept-Ranges: bytes"); 
header("Cache-Control: Expires ".gmdate("D, j M Y H:i:s e", $info['mtime']+2592000)); 
header("Content-Type: text/html"); 

ob_start("ob_gzhandler"); 
echo file_get_contents($filename); 
ob_end_flush(); 

Sto avendo due problemi in questo momento. Il primo è che ho difficoltà a determinare la dimensione risultante del file compresso per informare il browser della lunghezza del contenuto. Normalmente, vorrei includere questa linea:

header("Content-Length: ".$info["size"]); 

Ma, se lo faccio, il browser si blocca durante il tentativo di attendere ulteriori dati. C'è un modo per calcolare la dimensione totale? O dovrei ignorare questa direttiva header.

L'altro problema è che ogni volta che visualizzo questo file PHP in Firefox, tenta di farmi scaricare il risultato. In Chrome, mostra semplicemente come mi aspetterei. Eventuali suggerimenti?

Edit: Grazie a Soapbox, ho sostituito alla fine del codice con questo:

header("Content-Encoding: gzip"); 
$compressed = gzencode(file_get_contents($filename), 5); 
header("Content-Length: ".strlen($compressed)); 
die($compressed); 

Questa grande opera per il contenuto di lunghezza! Ma sto ancora scaricando Firefox per scaricare il file anziché visualizzarlo. :(

Edit Anche in questo caso: Ecco la fine del codice modificato, per gentile concessione di Cletus

// Start buffered output 
ob_start(); 
// Check for gzip capability 
if (stripos($_SERVER['HTTP_ACCEPT_ENCODING'], "gzip") !== false) { 
    ob_start("ob_gzhandler"); 
    echo file_get_contents($filename); 
    ob_end_flush(); 
} else 
    echo file_get_contents($filename); 

// Write the content length 
header('Content-Length: '.ob_get_length()); 
ob_end_flush(); 

ho intenzione di iniziare una nuova domanda per capire il motivo per cui Firefox continua a cercare di. scarica il file.

+0

Alcune delle intestazioni citate nelle risposte hanno alcuni errori; Il doppio Cache-Control, la linea con Cache-Control: Expires .... dovrebbe essere Expires: ... E Etag dovrebbe essere ETag. –

risposta

18

Il problema qui è che per conoscere la lunghezza del contenuto è necessario sapere se il client supporta la codifica gzip oppure se l'utente ha delegato tale decisione utilizzando ob_gzhandler.Da HTTP Headers:

ob_start(); 
ob_start('ob_gzhandler'); 

    ... output the page content... 

ob_end_flush(); // The ob_gzhandler one 

header('Content-Length: '.ob_get_length()); 

ob_end_flush(); // The main one 

Versione completa:

$filename = "style.css"; 

if (!file_exists($filename) || !($info = stat($filename))) { 
    header("HTTP/1.1 404 Not Found"); 
    die(); 
} 

header("Date: ".gmdate("D, j M Y H:i:s e", time())); 
header("Cache-Control: max-age=2592000"); 
header("Last-Modified: ".gmdate("D, j M Y H:i:s e", $info['mtime'])); 
header("ETag: ".sprintf("\"%x-%x-%x\"", $info['ino'], $info['size'], $info['mtime'])); 
header("Accept-Ranges: bytes"); 
header("Expires: ".gmdate("D, j M Y H:i:s e", $info['mtime']+2592000)); 
header("Content-Type: text/css"); // note: this was text/html for some reason? 

ob_start(); 
ob_start("ob_gzhandler"); 
echo file_get_contents($filename); 
ob_end_flush(); 
header('Content-Length: '.ob_get_length()); 
ob_end_flush(); 

Questo è molto meglio che prendere sulla questione codifica gzip da soli.

1

È necessario prima eseguire l'intero gzip e misurare il risultato (tenere i contenuti in memoria, o scriverli su disco mentre si comprime e quindi fare il file gzip), quindi scrivere il contenuto -Lunga intestazione e quindi inviare il contenuto del file

Oppure utilizzare chunked transfer encoding.

+0

La funzione 'file_put_contents' dovrebbe funzionare su di essa. – hyip

1

Per risolvere il problema di Firefox, penso che sia necessario includere header("Content-Encoding: gzip"); in modo che il browser sappia decomprimere il contenuto.

Per quanto riguarda la lunghezza del contenuto, è possibile provare a lasciare questo valore, o provare a capire un modo per utilizzare "Transfer-Encoding: chunked" (non è possibile inviare questa intestazione, è necessario formattare il dati appositamente per esso). È possibile che ob_end_flush abiliti automaticamente il chunking.

Ti consiglio di ottenere wireshark e catturare ciò che il tuo script php sta inviando e confrontarlo con un server che si comporta correttamente per vedere quali intestazioni, ecc. Sono mancanti.

+0

Questo è ciò che ob_start ("ob_gzhandler") fa se il client lo supporta. – cletus