Gzipped 파일의 컨텐츠 길이를 결정하는 방법은 무엇입니까?
-
03-07-2019 - |
문제
지금은 서버에서 CSS 및 JS 파일을 제공하려고 노력하고 있습니다. mod_gzip
또는 mod_deflate
. 그래서 나는 GZIP로 압축하고 사용자에게 돌아가기 위해 작은 PHP 스크립트를 작성했습니다.
예제 코드 :
$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();
지금 두 가지 문제가 있습니다. 첫 번째는, 컨텐츠 길이의 브라우저에 알리기 위해 압축 파일의 결과 크기를 결정하는 데 어려움이 있습니다. 일반적 으로이 줄을 포함시킬 것입니다.
header("Content-Length: ".$info["size"]);
그러나 만약 그렇다면 더 많은 데이터를 기다리려고 노력하면서 브라우저가 매달려 있습니다. 총 크기를 계산하는 방법이 있습니까? 또는이 헤더 지침을 무시해야합니다.
다른 문제는 Firefox 에서이 PHP 파일을 볼 때마다 결과를 다운로드하려고합니다. Chrome에서는 내가 기대할 수있는 것처럼 표시됩니다. 제안이 있습니까?
편집하다: SOAPBox 덕분에 코드의 끝을 다음과 같이 대체했습니다.
header("Content-Encoding: gzip");
$compressed = gzencode(file_get_contents($filename), 5);
header("Content-Length: ".strlen($compressed));
die($compressed);
이것은 컨텐츠 길이에 적합합니다! 그러나 나는 여전히 파일을 표시하는 대신 파일을 다운로드 할 Firefox를 얻고 있습니다. :(
다시 편집 : 다음은 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();
Firefox가 파일을 계속 다운로드하려고하는 이유를 알아 내기 위해 새로운 질문을 시작할 것입니다.
해결책
여기서 문제는 콘텐츠 길이를 알아야한다는 것입니다. 클라이언트가 GZIP 인코딩을 지원하는지 여부를 알아야하며 OB_GZHANDLER를 사용하여 해당 결정을 위임 한 것입니다. 에서 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
완전한 버전 :
$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();
이것은 많이 GZIP 인코딩 문제를 직접 복용하는 것보다 낫습니다.
다른 팁
먼저 전체 GZIP를 수행하고 결과를 측정해야합니다 (내용을 메모리에 고정하거나 압축 할 때 디스크에 쓰기 한 다음 GZIPPED 파일을 통계) 한 다음 컨텐츠 길이 헤더를 작성한 다음 파일을 보냅니다. 내용물.
또는 사용 청크 전송 인코딩.
Firefox 문제를 해결하려면 header( "Content-Encoding: gzip" );
브라우저가 컨텐츠를 압축하는 것을 알고 있습니다.
컨텐츠 길이는이 값을 꺼내거나 "전송 인코딩 : 청크"를 사용하는 방법을 찾으려고 시도 할 수 있습니다 (JSUT이 헤더를 보낼 수 없으므로 데이터를 특별히 포맷해야합니다. ). 가능합니다 ob_end_flush
청킹을 자동으로 활성화합니다.
Wireshark를 얻고 PHP 스크립트가 보내는 것을 캡처하고 올바르게 작동하는 서버와 비교하여 누락 된 헤더 등을 확인하는 것이 좋습니다.