發送 HTTP HEAD 請求(不下載主體),檢查回應狀態碼是否為 200 OK。若伺服器不支援 HEAD,退而使用 GET 並立即關閉連線。
<?php
/**
* 檢查遠端檔案是否存在
* @param string $url 完整 URL
* @param int $timeout 連線逾時秒數
* @return bool|array false=不存在/錯誤, array=[size, content-type, last-modified]
*/
function remoteFileExists($url, $timeout = 10) {
$parsed = parse_url($url);
if (!$parsed || !isset($parsed['host'])) {
return false;
}
$host = $parsed['host'];
$path = isset($parsed['path']) ? $parsed['path'] : '/';
if (!empty($parsed['query'])) {
$path .= '?' . $parsed['query'];
}
$port = isset($parsed['port']) ? $parsed['port'] : (($parsed['scheme'] ?? 'http') === 'https' ? 443 : 80);
$useSSL = ($parsed['scheme'] ?? '') === 'https';
$fp = @fsockopen(($useSSL ? 'ssl://' : '') . $host, $port, $errno, $errstr, $timeout);
if (!$fp) {
return false;
}
stream_set_timeout($fp, $timeout);
// 先嘗試 HEAD
$request = "HEAD $path HTTP/1.1\r\n";
$request .= "Host: $host\r\n";
$request .= "Connection: Close\r\n\r\n";
fwrite($fp, $request);
$headers = '';
while (!feof($fp)) {
$line = fgets($fp, 1024);
$headers .= $line;
if ($line === "\r\n") break;
}
// 解析狀態碼
if (!preg_match('/HTTP\/\d\.\d\s+(\d{3})/', $headers, $m)) {
fclose($fp);
return false;
}
$status = (int)$m[1];
// 若 HEAD 失敗(405/501),改用 GET 但只讀標頭
if ($status >= 400) {
fclose($fp);
$fp = @fsockopen(($useSSL ? 'ssl://' : '') . $host, $port, $errno, $errstr, $timeout);
if (!$fp) return false;
stream_set_timeout($fp, $timeout);
$request = "GET $path HTTP/1.1\r\nHost: $host\r\nConnection: Close\r\n\r\n";
fwrite($fp, $request);
$headers = '';
while (!feof($fp)) {
$line = fgets($fp, 1024);
$headers .= $line;
if ($line === "\r\n") break;
}
if (!preg_match('/HTTP\/\d\.\d\s+(\d{3})/', $headers, $m)) {
fclose($fp);
return false;
}
$status = (int)$m[1];
}
fclose($fp);
if ($status !== 200) {
return false;
}
// 擷取有用標頭
$info = ['size' => null, 'type' => null, 'modified' => null];
if (preg_match('/Content-Length:\s*(\d+)/i', $headers, $m)) $info['size'] = (int)$m[1];
if (preg_match('/Content-Type:\s*([^\r\n]+)/i', $headers, $m)) $info['type'] = trim($m[1]);
if (preg_match('/Last-Modified:\s*([^\r\n]+)/i', $headers, $m)) $info['modified'] = strtotime(trim($m[1]));
return $info;
}
// 測試
$url = 'https://www.example.com/robots.txt';
$result = remoteFileExists($url);
if ($result) {
echo "存在!大小: " . ($result['size'] ? number_format($result['size']) . ' bytes' : '未知');
echo " 類型: " . ($result['type'] ?? '未知');
} else {
echo "不存在或無法存取";
}
?>
function remoteFileExistsCurl($url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => false, // 生產環境請設 true 並配置 CA
CURLOPT_HEADER => true,
CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $code === 200;
}
$headers = @get_headers($url, 1);
return $headers && strpos($headers[0], '200') !== false;
robots.txt 與速率限制,避免被封禁。302 重導向,需啟用 FOLLOWLOCATION。