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

解決 file_get_contents() "failed to open stream: HTTP request failed" 錯誤完整指南

     編輯:PHP 相關知識

file_get_contents() 遠端請求失敗:原因分析與解決方案

file_get_contents() 讀取遠端 URL 時出現 Warning: file_get_contents(...): failed to open stream: HTTP request failed! 是常見問題。本文系統梳理可能原因,並提供分級解決方案。

錯誤訊息解讀

Warning: file_get_contents(https://example.com/file.rar): 
failed to open stream: HTTP request failed! 
in /path/to/script.php on line 10

表示 HTTP 請求過程中發生錯誤,導致無法取得回應內容。後續通常伴隨 404 Not Found、403 Forbidden、500 Internal Server Error、Connection timeout 等具體 HTTP 狀態碼。

常見原因分類表

類別具體原因檢查方法
伺服器端檔案不存在 (404)瀏覽器直接開啟 URL 確認
權限不足 (403) / 需登入 (401)檢查 Referer、User-Agent、Cookie、Token
伺服器錯誤 (5xx) / 限流 (429)稍後重試、檢查 API 文件限制
網路/連線DNS 解析失敗dig example.com / nslookup
連線逾時預設 60 秒,大檔/慢線路易觸發
SSL/TLS 憑證驗證失敗自簽憑證、憑證過期、主機名不符
防火牆/代理阻擋伺服器出口 IP 被封、需走 Proxy
PHP 設定allow_url_fopen = Offphpinfo() 或 ini_get('allow_url_fopen')
default_socket_timeout 過短預設 60 秒,大檔需調大
OpenSSL 擴充套件未啟用HTTPS 請求需 extension=openssl
請求參數User-Agent 被攔截、缺少必要 Header、Redirect 循環模擬瀏覽器 Header、限制重導向次數

分級解決方案

層級 1:PHP 內建串流上下文優化(零依賴)

<?php
$url = 'https://example.com/large.rar';

$context = stream_context_create([
    'http' => [
        'method' => 'GET',
        'timeout' => 300, // 5 分鐘逾時
        'header' => implode("\r\n", [
            'User-Agent: Mozilla/5.0 (compatible; PHP Bot)',
            'Accept: */*',
            'Accept-Encoding: gzip, deflate', // 支援壓縮
        ]),
        'follow_location' => 1, // 支援重導向
        'max_redirects' => 10,
        'ignore_errors' => true, // 即使 4xx/5xx 也取得內容
    ],
    'ssl' => [
        'verify_peer' => true,
        'verify_peer_name' => true,
        // 若對方自簽憑證且信任,可暫時關閉驗證(生產環境慎用)
        // 'verify_peer' => false,
        // 'verify_peer_name' => false,
    ],
]);

$content = @file_get_contents($url, false, $context);

if ($content === false) {
    $error = error_get_last();
    throw new Exception("下載失敗: {$error['message']}");
}

// 取得 HTTP 狀態碼
$meta = stream_get_meta_data($handle ?? fopen('php://memory', 'r'));
// 實際需從 $http_response_header 取得
$statusLine = $http_response_header[0] ?? 'HTTP/1.1 000';
preg_match('/\d{3}/', $statusLine, $matches);
$statusCode = (int)($matches[0] ?? 0);

if ($statusCode >= 400) {
    throw new Exception("HTTP 錯誤: $statusCode");
}

file_put_contents('downloaded.rar', $content);
?>

層級 2:使用 cURL(功能最完整、效能最佳)

<?php
function downloadWithCurl(string $url, string $savePath, array $options = []): bool {
    $ch = curl_init();
    
    $defaultOptions = [
        CURLOPT_URL => $url,
        CURLOPT_FILE => fopen($savePath, 'w'), // 直接寫入檔案,不佔記憶體
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 10,
        CURLOPT_TIMEOUT => 300,
        CURLOPT_CONNECTTIMEOUT => 30,
        CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PHP Bot)',
        CURLOPT_ENCODING => 'gzip, deflate', // 自動解壓
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_FAILONERROR => false, // 即使 4xx 也寫入內容
    ];
    
    curl_setopt_array($ch, array_merge($defaultOptions, $options));
    
    $result = curl_exec($ch);
    $errno = curl_errno($ch);
    $error = curl_error($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    fclose(curl_getinfo($ch, CURLINFO_FILE)); // 關閉檔案指標
    curl_close($ch);
    
    if ($errno) {
        @unlink($savePath); // 失敗刪除不完整檔案
        throw new Exception("cURL 錯誤 ($errno): $error");
    }
    
    if ($httpCode >= 400) {
        @unlink($savePath);
        throw new Exception("HTTP $httpCode");
    }
    
    return true;
}

// 使用
try {
    downloadWithCurl('https://example.com/file.rar', 'file.rar');
    echo "下載完成\n";
} catch (Exception $e) {
    echo "失敗: " . $e->getMessage() . "\n";
}
?>

層級 3:Guzzle HTTP Client(現代專案首選,支援非同步、中介軟體)

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

$client = new Client([
    'timeout' => 300,
    'verify' => true, // SSL 驗證
]);

try {
    // 串流下載大檔案,不佔記憶體
    $response = $client->request('GET', 'https://example.com/file.rar', [
        'sink' => 'file.rar', // 直接存檔
        'headers' => ['User-Agent' => 'MyApp/1.0'],
        'on_stats' => function ($transferStats) {
            // 進度條回呼
            if ($transferStats->getTransferSize() > 0) {
                $pct = round($transferStats->getBytesTransferred() / $transferStats->getTransferSize() * 100, 1);
                echo "\r進度: $pct%";
            }
        },
    ]);
    echo "\n下載完成,狀態碼: " . $response->getStatusCode() . "\n";
} catch (RequestException $e) {
    echo "請求失敗: " . $e->getMessage() . "\n";
    if ($e->hasResponse()) {
        echo "狀態碼: " . $e->getResponse()->getStatusCode() . "\n";
    }
}
?>

針對 RAR/二進位檔案的特殊注意事項

  • 二進位安全:file_get_contents、cURL、Guzzle 皆支援二進位,無需特殊處理。
  • Content-Encoding:若伺服器回傳 Content-Encoding: gzip 但檔案實為 RAR(非 gzip),需停用自動解壓:cURL 設 CURLOPT_ENCODING => '',Guzzle 設 'decode_content' => false。
  • 斷點續傳:大檔建議支援 Range 標頭,cURL/Guzzle 均可實作。

快速診斷腳本

<?php
// 診斷腳本:在伺服器上執行此檔案
$testUrls = [
    'https://httpbin.org/get',           // 正常 HTTPS
    'https://httpbin.org/status/404',    // 404
    'https://httpbin.org/status/500',    // 500
    'https://expired.badssl.com/',       // 憑證過期
    'https://self-signed.badssl.com/',   // 自簽憑證
];

foreach ($testUrls as $url) {
    echo "測試: $url\n";
    $ctx = stream_context_create(['http' => ['timeout' => 10, 'ignore_errors' => true]]);
    $start = microtime(true);
    $content = @file_get_contents($url, false, $ctx);
    $elapsed = round((microtime(true) - $start) * 1000);
    
    $status = $http_response_header[0] ?? '無回應';
    echo "  狀態: $status\n";
    echo "  耗時: {$elapsed}ms\n";
    echo "  長度: " . ($content !== false ? strlen($content) : '失敗') . "\n";
    echo "  錯誤: " . (error_get_last()['message'] ?? '無') . "\n";
    echo "\n";
}
?>

總結建議

  1. 優先檢查 allow_url_fopen=On、OpenSSL 啟用、default_socket_timeout 足夠大。
  2. 使用 stream_context_create() 設定 User-Agent、逾時、忽略錯誤、SSL 選項。
  3. 生產環境強烈建議改用 cURL 或 Guzzle:功能更豐富、錯誤資訊更詳細、支援串流下載大檔不佔記憶體、支援斷點續傳、非同步並發。
  4. 下載二進位檔案(RAR、ZIP、圖片)務必使用「寫入檔案」模式(cURL CURLOPT_FILE、Guzzle sink),避免記憶體爆炸。
  5. 建立統一的下載服務類,封裝重試、日誌、進度、校驗(MD5/SHA256)等通用邏輯。

遇到「HTTP request failed」不要只看錯誤訊息,善用 $http_response_header、cURL curl_getinfo()、Guzzle 例外物件取得完整 HTTP 狀態碼與回應標頭,才能精準定位問題根因。

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