Come determinare la lunghezza del contenuto di un file gzip?
-
03-07-2019 - |
Domanda
In questo momento sto cercando di fornire file CSS e JS da un server che non mi consente di abilitare mod_gzip
o mod_deflate
. Quindi ho scritto un piccolo script PHP da comprimere con GZIP e tornare all'utente.
Codice di esempio:
$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();
Al momento sto riscontrando due problemi. Il primo è che sto riscontrando problemi nel determinare la dimensione risultante del file compresso per informare il browser della lunghezza del contenuto. Normalmente, includerei questa riga:
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 dell'intestazione.
L'altro problema è, ogni volta che visualizzo questo file PHP in Firefox, prova a farmi scaricare il risultato. In Chrome, lo mostra semplicemente come mi aspetterei. Qualche suggerimento?
Modifica: Grazie a SoapBox, ho sostituito la fine del codice con questa:
header("Content-Encoding: gzip");
$compressed = gzencode(file_get_contents($filename), 5);
header("Content-Length: ".strlen($compressed));
die($compressed);
Funziona benissimo per la lunghezza dei contenuti! Ma sto ancora facendo in modo che Firefox scarichi il file anziché visualizzarlo. : (
Modifica di nuovo: ecco il codice di fine codice modificato, per gentile concessione di Cletus.
// Start buffered output
ob_start();
// Check for gzip capability
if (stripos( In questo momento sto cercando di fornire file CSS e JS da un server che non mi consente di abilitare mod_gzip
o mod_deflate
. Quindi ho scritto un piccolo script PHP da comprimere con GZIP e tornare all'utente.
Codice di esempio:
$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();
Al momento sto riscontrando due problemi. Il primo è che sto riscontrando problemi nel determinare la dimensione risultante del file compresso per informare il browser della lunghezza del contenuto. Normalmente, includerei questa riga:
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 dell'intestazione.
L'altro problema è, ogni volta che visualizzo questo file PHP in Firefox, prova a farmi scaricare il risultato. In Chrome, lo mostra semplicemente come mi aspetterei. Qualche suggerimento?
Modifica: Grazie a SoapBox, ho sostituito la fine del codice con questa:
header("Content-Encoding: gzip");
$compressed = gzencode(file_get_contents($filename), 5);
header("Content-Length: ".strlen($compressed));
die($compressed);
Funziona benissimo per la lunghezza dei contenuti! Ma sto ancora facendo in modo che Firefox scarichi il file anziché visualizzarlo. : (
Modifica di nuovo: ecco il codice di fine codice modificato, per gentile concessione di Cletus.
<*>
Inizierò una nuova domanda per capire perché Firefox continua a provare a scaricare il file.
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();
Inizierò una nuova domanda per capire perché Firefox continua a provare a scaricare il file.
Soluzione
Il problema qui è che per conoscere la lunghezza del contenuto devi sapere se il client supporta la codifica gzip e hai delegato quella decisione usando ob_gzhandler. Da Intestazioni HTTP :
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 affrontare il problema della codifica gzip da soli.
Altri suggerimenti
Devi prima fare l'intero gzip e misurare il risultato (o mantenendo i contenuti in memoria, o scrivendoli sul disco mentre comprimi e poi stating il file gzip), quindi scrivi l'intestazione Content-Length e poi invia il contenuto del file.
Oppure usa codifica di trasferimento chunked .
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, puoi provare a lasciare questo valore disattivato o provare a trovare un modo per usare " Transfer-Encoding: chunked " (non è possibile inviare questa intestazione, è necessario formattare i dati appositamente per essa). È possibile che ob_end_flush
abiliti automaticamente il chunking.
Ti consiglio di ottenere WireShark e acquisire ciò che lo script php sta inviando e confrontarlo con un server che si comporta correttamente per vedere quali intestazioni, ecc. mancano.