14

現在、有効にできないサーバーから 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();

私は今2つの問題を抱えています。1 つ目は、コンテンツの長さをブラウザに通知するための圧縮ファイルの結果サイズを判断するのに問題があることです。通常、次の行を含めます。

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

しかし、そうすると、さらにデータを待つ間にブラウザーがハングします。合計サイズを計算する方法はありますか? または、このヘッダー ディレクティブを無視する必要があります。

もう 1 つの問題は、この PHP ファイルを Firefox で表示するたびに、結果をダウンロードしようとすることです。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 がファイルのダウンロードを試行し続ける理由を理解するために、新しい質問を開始します。

4

3 に答える 3

19

ここでの問題は、コンテンツの長さを知るには、クライアントが 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 エンコーディングの問題を自分で引き受けるよりもはるかに優れています。

于 2009-05-03T01:28:04.127 に答える
1

最初にgzip全体を実行して結果を測定する必要があります(コンテンツをメモリに保持するか、圧縮時にディスクに書き込んでからgzipファイルを開始します)、次にContent-Lengthヘッダーを書き込んでからファイルを送信しますコンテンツ。

または、チャンク転送エンコーディングを使用します。

于 2009-05-03T00:12:24.963 に答える
1

header( "Content-Encoding: gzip" );Firefox の問題を解決するには、ブラウザーがコンテンツを解凍することを認識できるようにする必要があると思います。

コンテンツの長さについては、この値をオフのままにしておくか、「Transfer-Encoding: chunked」を使用する方法を見つけてみてください (このヘッダーを送信するだけではできません。データを特別にフォーマットする必要があります)。 )。ob_end_flushチャンクを自動的に有効にすることが可能です。

Wireshark を入手して、php スクリプトが送信する内容をキャプチャし、適切に動作しているサーバーと比較して、欠落しているヘッダーなどを確認することをお勧めします。

于 2009-05-03T00:15:48.123 に答える