如何确定gzip压缩文件的内容长度?

时间:2009-05-03 00:08:29

标签: php http-headers gzip header

现在我正在尝试从服务器提供CSS和JS文件,这些文件不允许我启用mod_gzipmod_deflate。所以我写了一个小的PHP脚本来压缩GZIP并返回给用户。

示例代码:

$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会继续尝试下载该文件。

3 个答案:

答案 0 :(得分:18)

这里的问题是要了解内容长度,您需要知道客户端是否支持gzip编码,并且您已使用ob_gzhandler委派了该决策。来自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

完整版:

$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编码问题更好。

答案 1 :(得分:1)

您需要首先执行整个gzip并测量结果(将内容保存在内存中,或者在压缩然后统计gzip压缩文件时将它们写入磁盘),然后编写Content-Length标头然后发送文件内容。

或使用chunked transfer encoding

答案 2 :(得分:1)

要解决你的firefox问题,我认为你需要包含header( "Content-Encoding: gzip" );,以便浏览器知道解压缩内容。

至于内容长度,您可以尝试关闭此值,或尝试找出使用“Transfer-Encoding:chunked”的方法(您不能jsut发送此标头,您需要格式化数据特别适合它)。 ob_end_flush可能会自动启用分块。

我建议您获取wireshark并捕获您的PHP脚本发送的内容,并将其与正常运行的服务器进行比较,以查看缺少哪些标头等。