傳統獲取遠程圖片大小需下載完整內容再 strlen(),效率極低。本文介紹發送 HTTP HEAD 請求讀取 Content-Length 響應頭的高效方案,附 fsockopen 完整實現與現代替代方案。
HTTP HEAD 請求僅返回響應頭,不包含實體內容。服務器對靜態資源通常返回 Content-Length 表示文件大小(字節數)。
<?php
function getRemoteFileSize($host, $path, $port = 80, $timeout = 10) {
$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$fp) {
return ['error' => "$errstr ($errno)"];
}
// 構造 HEAD 請求
$out = "HEAD $path HTTP/1.1\r\n";
$out .= "Host: $host\r\n";
$out .= "Connection: Close\r\n\r\n";
fwrite($fp, $out);
$size = null;
while (!feof($fp)) {
$header = fgets($fp, 1024);
if (stripos($header, 'Content-Length:') !== false) {
$size = (int)trim(substr($header, strpos($header, ':') + 1));
break; // 找到即可停止讀取
}
}
fclose($fp);
return $size !== null ? ['size' => $size] : ['error' => 'Content-Length header not found'];
}
// 使用示例
$result = getRemoteFileSize('www.baidu.com', '/img/baidu_sylogo1.gif');
if (isset($result['size'])) {
echo "圖片大小: {$result['size']} bytes (" . round($result['size']/1024, 2) . " KB)";
} else {
echo "錯誤: {$result['error']}";
}
?>
HEAD /path HTTP/1.1 (必須包含 Host 頭)\r\n (非 \n)Content-Length 即 break,不讀取剩餘頭fsockopen('ssl://host', 443) 或改用 cURL/流上下文function getSizeCurl($url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true, // HEAD 請求
CURLOPT_HEADER => true, // 包含頭
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true, // 跟隨重定向
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => false, // 開發環境
]);
curl_exec($ch);
$size = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
curl_close($ch);
return $size > 0 ? $size : null;
}
$headers = get_headers('https://example.com/image.jpg', 1);
// 返回關聯數組時,Content-Length 可能被合併為數組
$size = $headers['Content-Length'] ?? ($headers['Content-Length'][0] ?? null);
/*
注意:get_headers() 默認發送 GET 請求!
PHP 7.1+ 可用 stream_context 設置 method:
$ctx = stream_context_create(['http' => ['method' => 'HEAD']]);
$headers = get_headers($url, 1, $ctx);
*/
$ctx = stream_context_create([
'http' => [
'method' => 'HEAD',
'timeout' => 10,
'header' => 'User-Agent: PHP/' . PHP_VERSION,
],
'ssl' => ['verify_peer' => false, 'verify_peer_name' => false]
]);
$fp = fopen($url, 'r', false, $ctx);
$meta = stream_get_meta_data($fp);
fclose($fp);
// 解析 $meta['wrapper_data'] 獲取頭信息
foreach ($meta['wrapper_data'] as $header) {
if (stripos($header, 'Content-Length:') === 0) {
return (int)trim(substr($header, 15));
}
}
| 方案 | 優點 | 缺點 | 推薦場景 |
|---|---|---|---|
| fsockopen | 無依賴、底層可控 | 手動處理協議、HTTPS 麻煩 | 教學、極簡環境 |
| cURL | 功能全、自動重定向、HTTPS 友好、性能好 | 需擴展 | 生產環境首選 |
| stream_context | 原生、面向對象流操作 | 代碼較繁瑣 | 不想裝 cURL 時 |
| get_headers | 一行代碼 | 默認 GET、行為不一致 | 快速腳本、已知支持 HEAD |
Transfer-Encoding: chunkedReferer、User-Agent、CookieCURLOPT_FOLLOWLOCATIONContent-Length 可能超過 32 位整數範圍,用 float 或 CURLINFO_CONTENT_LENGTH_DOWNLOAD_T (cURL 7.55+)採用 cURL + HEAD 請求是獲取遠程文件大小的工業級標準做法。