發送 HTTP HEAD 請求(不下載 Body),檢查響應狀態碼:
200 OK → 文件存在404 Not Found → 文件不存在3xx → 重定向,可選擇跟隨或視為存在<?php
/**
* 檢查遠程文件/URL 是否可訪問(返回 2xx/3xx)
*
* @param string $url 目標 URL
* @param int $timeout 連接超時秒數
* @param bool $followRedirects 是否跟隨重定向
* @return bool
*/
function checkRemoteFileExists(string $url, int $timeout = 10, bool $followRedirects = true): bool
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true, // HEAD 請求
CURLOPT_RETURNTRANSFER => true, // 不直接輸出
CURLOPT_FOLLOWLOCATION => $followRedirects, // 跟隨重定向
CURLOPT_MAXREDIRS => 5, // 最大重定向次數
CURLOPT_CONNECTTIMEOUT => $timeout, // 連接超時
CURLOPT_TIMEOUT => $timeout, // 總超時
CURLOPT_SSL_VERIFYPEER => false, // 生產環境建議 true + CA 憑證
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_USERAGENT => 'Mozilla/5.0 (PHP File Checker)', // 部分站點拒絕默認 UA
CURLOPT_HEADER => false, // 不需要 Header 內容
]);
$result = curl_exec($ch);
if ($result === false) {
// cURL 錯誤:DNS 失敗、連接超時、SSL 驗證失敗等
error_log("cURL error for {$url}: " . curl_error($ch));
curl_close($ch);
return false;
}
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// 2xx 成功、3xx 重定向(視為存在)
return $statusCode >= 200 && $statusCode < 400;
}
// 使用範例
$urls = [
'https://example.com/image.jpg',
'https://example.com/not-exist.pdf',
'https://httpbin.org/status/404',
'https://httpbin.org/redirect/2',
];
foreach ($urls as $url) {
$exists = checkRemoteFileExists($url, 5);
echo "{$url} => " . ($exists ? '存在' : '不存在/錯誤') . "\n";
}
<?php
function getRemoteFileInfo(string $url, int $timeout = 10): ?array
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_CONNECTTIMEOUT => $timeout,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_HEADER => true, // 需要 Header 解析
]);
$response = curl_exec($ch);
if ($response === false) {
curl_close($ch);
return null;
}
$info = [
'url' => $url,
'http_code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
'content_type' => curl_getinfo($ch, CURLINFO_CONTENT_TYPE),
'content_length' => curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD),
'effective_url' => curl_getinfo($ch, CURLINFO_EFFECTIVE_URL), // 重定向後最終 URL
'filetime' => curl_getinfo($ch, CURLINFO_FILETIME), // 文件修改時間(Unix 時間戳,-1 表示未知)
'connect_time' => curl_getinfo($ch, CURLINFO_CONNECT_TIME), // 連接耗時
'total_time' => curl_getinfo($ch, CURLINFO_TOTAL_TIME), // 總耗時
];
curl_close($ch);
return $info;
}
// 使用
$info = getRemoteFileInfo('https://example.com/large.zip');
if ($info && $info['http_code'] === 200) {
echo "文件大小: " . ($info['content_length'] / 1024 / 1024) . " MB\n";
echo "類型: {$info['content_type']}\n";
echo "最終 URL: {$info['effective_url']}\n";
}
<?php
function checkMultipleUrls(array $urls, int $timeout = 10, int $concurrency = 10): array
{
$mh = curl_multi_init();
$handles = [];
$results = [];
// 初始化所有 handle
foreach ($urls as $i => $url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_NOBODY => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_CONNECTTIMEOUT => $timeout,
CURLOPT_TIMEOUT => $timeout,
]);
curl_multi_add_handle($mh, $ch);
$handles[$i] = $ch;
}
// 並發執行
$running = null;
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh, 0.1); // 等待活動
} while ($running > 0);
// 收集結果
foreach ($handles as $i => $ch) {
$error = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$results[$urls[$i]] = [
'exists' => $error === '' && $code >= 200 && $code < 400,
'http_code' => $code,
'error' => $error ?: null,
];
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_cleanup($mh);
return $results;
}
// 使用
$urls = array_fill(0, 100, 'https://httpbin.org/status/200'); // 測試 100 個
$start = microtime(true);
$results = checkMultipleUrls($urls, 5, 20);
echo "耗時: " . (microtime(true) - $start) . " 秒\n";
// 串行約 500 秒,並發 20 約 25 秒
| 方法 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
get_headers() |
原生函數、代碼簡潔 | 無超時控制、難處理重定向、需 allow_url_fopen=On |
簡單腳本、內網可信環境 |
file_get_contents() |
極簡單 | 下載整個 Body、無超時、失敗拋警告、內存佔用大 | 極小文件、不推薦 |
| cURL (HEAD) | 完整控制、不下載 Body、支援並發、超時、重定向、SSL | 代碼較長、需 cURL 擴展 | 生產環境首選 |
| Guzzle/HTTP Client | 面向對象、中間件、異步、PSR-18 | 依賴 Composer、較重 | 現代框架專案 |
CURLOPT_SSL_VERIFYPEER => true + CURLOPT_CAINFO => '/path/to/cacert.pem'curl_errno())便於排查。200 OK 文件存在
301/302/307/308 Redirect 重定向(跟隨後通常存在)
403 Forbidden 存在但無權限
404 Not Found 不存在
410 Gone 永久刪除
5xx Server Error 服務器錯誤(重試可能成功)
掌握 cURL HEAD 請求與並發多工,能高效、可靠地實現遠程文件存在性檢查、鏈接有效性掃描、CDN 緩存預熱等場景。