首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 利用 cURL 判斷遠程文件是否存在:HEAD 請求、狀態碼檢查與錯誤處理最佳實踐

     編輯:PHP 相關知識

核心原理

發送 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、較重 現代框架專案

生產環境強化清單

  1. SSL 驗證:CURLOPT_SSL_VERIFYPEER => true + CURLOPT_CAINFO => '/path/to/cacert.pem'
  2. 超時策略:連接 5s、總計 10s,防止掛死。
  3. User-Agent:設置可識別 UA,避免被反爬蟲攔截。
  4. 錯誤日誌:記錄 cURL 錯誤碼(curl_errno())便於排查。
  5. 頻率限制:大量檢查時加入 Token Bucket 或固定窗口限流。
  6. 緩存結果:同一 URL 短時間內重複檢查,緩存結果 1-5 分鐘。

常見 HTTP 狀態碼含義速查

200 OK                    文件存在
301/302/307/308 Redirect  重定向(跟隨後通常存在)
403 Forbidden             存在但無權限
404 Not Found             不存在
410 Gone                  永久刪除
5xx Server Error          服務器錯誤(重試可能成功)

掌握 cURL HEAD 請求與並發多工,能高效、可靠地實現遠程文件存在性檢查、鏈接有效性掃描、CDN 緩存預熱等場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved