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

PHP 檔案讀寫核心 API 速查:fopen/fread/fwrite vs file_get_contents/put_contents 選型指南

     編輯:PHP 相關知識

PHP 提供兩套檔案操作 API:**資源流式 (fopen 系)** 與 **便利函式 (file_get_contents 系)**。本文詳細對比兩套 API 的記憶體模型、適用場景、錯誤處理差異,並提供生產級封裝範例。

核心 API 對比表

特性fopen/fread/fwritefile_get/put_contents
記憶體模型流式 (可逐塊讀寫)一次性載入/寫入完整內容
大檔案支援優秀 (常數記憶體)差 (O(n) 記憶體,易爆限制)
遠端 URL支援 (需 `allow_url_fopen`)支援 (同樣需設定)
二進制安全需 `b` 模式 (`rb`, `wb`)原生二進制安全
鎖定機制`flock()` 精細控制`FILE_APPEND | LOCK_EX` 旗標
資源管理手動 `fclose()`自動管理
效能 (小檔)稍慢 (函式呼叫開銷)極快 (C 級優化)

基礎讀寫範例

資源流式 (大檔、精確控制)

<?php
// 讀取大檔逐行處理 (記憶體常數)
$fp = fopen('huge.log', 'rb');
if ($fp) {
    while (($line = fgets($fp)) !== false) {
        // 處理單行
        processLine($line);
    }
    fclose($fp);
}

// 寫入大量資料流式
$fp = fopen('output.csv', 'wb');
if ($fp) {
    foreach ($hugeDataset as $row) {
        fputcsv($fp, $row);
    }
    fclose($fp);
}
?>

便利函式 (小檔、配置檔、快取)

<?php
// 讀取完整內容到字串
$content = file_get_contents('config.json');
$config = json_decode($content, true);

// 寫入/覆蓋 (原子性:臨時檔 + rename)
$tmp = tempnam(sys_get_temp_dir(), 'cfg_');
file_put_contents($tmp, json_encode($config, JSON_PRETTY_PRINT));
rename($tmp, 'config.json'); // 原子替換

// 追加模式 + 獨佔鎖
file_put_contents('access.log', $logLine . "\n", FILE_APPEND | LOCK_EX);
?>

進階封裝:統一介面檔案操作類

<?php
class FileUtil {
    // 安全讀取 (自動判斷大小選擇策略)
    public static function read(string $path, int $maxMemory = 10 * 1024 * 1024): string|false {
        $size = @filesize($path);
        if ($size === false) return false;
        
        if ($size <= $maxMemory) {
            return file_get_contents($path);
        }
        
        // 大檔流式讀取拼接 (或直接回傳 Generator)
        $chunks = [];
        $fp = fopen($path, 'rb');
        if (!$fp) return false;
        while (!feof($fp)) {
            $chunks[] = fread($fp, 8192);
        }
        fclose($fp);
        return implode('', $chunks);
    }

    // 原子寫入 (防止部分寫入被讀取)
    public static function write(string $path, string $data, bool $append = false): bool {
        $dir = dirname($path);
        if (!is_dir($dir)) mkdir($dir, 0755, true);
        
        $tmp = tempnam($dir, 'fw_');
        $flags = $append ? (FILE_APPEND | LOCK_EX) : LOCK_EX;
        $result = file_put_contents($tmp, $data, $flags);
        if ($result === false) {
            @unlink($tmp);
            return false;
        }
        return rename($tmp, $path);
    }

    // 安全 JSON 讀寫
    public static function readJson(string $path, mixed $default = null): mixed {
        $content = self::read($path);
        return $content ? json_decode($content, true) : $default;
    }
    public static function writeJson(string $path, mixed $data): bool {
        return self::write($path, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
    }
}
?>

遠端 URL 讀取特殊處理

<?php
function safeHttpGet(string $url, int $timeout = 10): string|false {
    $ctx = stream_context_create([
        'http' => [
            'timeout' => $timeout,
            'method' => 'GET',
            'header' => "User-Agent: PHP/" . PHP_VERSION . "\r\nAccept: */*\r\n",
            'ignore_errors' => true, // 取得 4xx/5xx 內容
        ],
        'ssl' => [
            'verify_peer' => true,
            'verify_peer_name' => true,
        ]
    ]);
    
    $content = @file_get_contents($url, false, $ctx);
    if ($content === false) return false;
    
    // 檢查 HTTP 狀態碼 (需 PHP 7.1+ $http_response_header)
    $statusLine = $http_response_header[0] ?? '';
    if (preg_match('/\s(\d{3})\s/', $statusLine, $m)) {
        $code = (int)$m[1];
        if ($code >= 400) {
            error_log("HTTP $code for $url");
            return false;
        }
    }
    return $content;
}
?>

選型決策樹


檔案大小 > 10MB 或 未知大小?
├─ 是 → fopen/fread/fwrite (流式)
└─ 否 → 需要精確鎖控/逐行處理/二進制模式?
    ├─ 是 → fopen/fread/fwrite
    └─ 否 → file_get/put_contents (簡潔、效能佳)

常見坑點提醒

  • file_get_contents 讀取大檔案導致 `Allowed memory size exhausted`。
  • fopen('php://input')` 只能讀取一次,二次讀取為空。
  • Windows 路徑分隔符:統一用 `/` 或 `DIRECTORY_SEPARATOR`。
  • 併發寫入必須用 `LOCK_EX` 或資料庫替代。
  • 包含使用者輸入的路徑必須 `realpath()` 驗證防止目錄遍歷。

根據檔案大小、併發需求、控制精度選擇適當 API,封裝統一工具類可顯著降低檔案操作 Bug 率。

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