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

PHP 複製遠端檔案:copy() 搭配錯誤處理與替代方案

     編輯:PHP 相關知識

PHP copy() 函數支援 URL 封裝協議(需 allow_url_fopen=On),可直接將遠端檔案下載至本地。但網路操作充滿不確定性(逾時、404、權限、磁碟空間不足),必須建立完善的錯誤處理機制。

基礎範例與錯誤捕獲

<?php
$remoteUrl = 'https://example.com/assets/logo.png';
$localPath = __DIR__ . '/downloads/logo.png';

// 確保目標目錄存在
$dir = dirname($localPath);
if (!is_dir($dir)) {
    mkdir($dir, 0755, true);
}

// 執行複製並捕獲錯誤
if (@copy($remoteUrl, $localPath)) {
    echo "下載成功:$localPath";
} else {
    // PHP 7+ 可用 error_get_last() 取得最後錯誤詳情
    $error = error_get_last();
    $msg = $error ? $error['message'] : '未知錯誤';
    echo "下載失敗:$msg";
}
?>

進階:自訂 HTTP 內容(Header、逾時、User-Agent)

copy() 無法自訂 HTTP 請求細節。需用 stream_context_create() 建立上下文:

<?php
$url = 'https://api.example.com/protected/file.zip';
$dest = '/tmp/file.zip';

$context = stream_context_create([
    'http' => [
        'method' => 'GET',
        'header' => "Authorization: Bearer YOUR_TOKEN\r\n" .
                    "User-Agent: MyApp/1.0\r\n",
        'timeout' => 30,          // 連線逾時(秒)
        'ignore_errors' => true,  // 即使 4xx/5xx 也取得內容
    ],
    'ssl' => [
        'verify_peer' => true,
        'verify_peer_name' => true,
    ],
]);

if (@copy($url, $dest, $context)) {
    echo "下載成功";
} else {
    $err = error_get_last();
    echo "失敗:" . ($err['message'] ?? '未知');
}
?>

替代方案:cURL(更強大、更穩健)

生產環境強烈建議使用 cURL,支援斷點續傳、進度回調、代理、憑證等高級功能:

<?php
function downloadWithCurl(string $url, string $dest, array $options = []): bool
{
    $fp = @fopen($dest, 'wb');
    if (!$fp) return false;

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_FILE => $fp,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 300, // 總逾時 5 分鐘
        CURLOPT_USERAGENT => 'MyApp/1.0',
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
    ] + $options);

    $result = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $errno = curl_errno($ch);
    $error = curl_error($ch);

    curl_close($ch);
    fclose($fp);

    if ($errno || $httpCode >= 400) {
        @unlink($dest); // 失敗刪除不完整檔案
        error_log("cURL 下載失敗 [$url]: HTTP $httpCode, cURL $errno: $error");
        return false;
    }
    return true;
}
?>

常見問題排查清單

  1. allow_url_fopen:php.ini 中必須啟用,否則 copy() 無法開啟 URL。
  2. SSL 憑證驗證:本地開發環境常缺少 CA 憑證包,導致 HTTPS 失敗。可暫時關閉驗證(僅測試用),或安裝 cacert.pem 並設定 openssl.cafile。
  3. 執行時間限制:大檔下載可能超過 max_execution_time,需 set_time_limit(0) 或調整 php.ini。
  4. 磁碟空間:下載前檢查 disk_free_space()。
  5. 檔案鎖定:多程序同時寫入同一檔案會損毀,建議下載至暫存檔(tempnam())完成後再 rename() 覆蓋(原子操作)。

最佳實踐總結

  • 簡單公開檔案:copy() + error_get_last() 足矣。
  • 需認證、大檔、高可靠:cURL 是唯一正解。
  • 永遠驗證下載後的檔案大小/雜湊值(MD5/SHA256),確保完整性。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved