將數據導出為文本文件是常見需求:日志歸檔、報表導出、接口調試存檔。PHP 提供底層 fopen/fwrite/fclose 流式操作與高層 file_put_contents 一鍵寫入,本文對比兩者優劣與適用場景。
<?php
$path = 'c:/export/data_' . date('Ymd_His') . '.txt';
// 打開模式:a=追加寫入(指針尾部),w=覆蓋寫入(指針頭部,截斷)
// a+ / w+ 同時可讀
$fp = fopen($path, 'a') or die("無法打開文件: $path");
// 模擬導出 GET/POST 參數
$data = [];
foreach (['_GET', '_POST'] as $super) {
foreach ($GLOBALS[$super] as $k => $v) {
$data[] = "[$k]=>$v";
}
}
$data[] = '---END---';
// 寫入 (可分多次 fwrite,內存常數)
foreach ($data as $line) {
fwrite($fp, $line . PHP_EOL);
}
fclose($fp);
echo "導出完成: $path";
| 模式 | 指針位置 | 文件存在 | 文件不存在 | 典型用途 |
|---|---|---|---|---|
w | 頭部 | 截斷(清空) | 創建 | 覆蓋式寫入、配置文件生成 |
w+ | 頭部 | 截斷 | 創建 | 讀寫覆蓋 |
a | 尾部 | 保留內容 | 創建 | 日志追加、多進程安全寫入 |
a+ | 尾部 | 保留 | 創建 | 讀寫追加 |
$fp = fopen($path, 'a');
if (flock($fp, LOCK_EX)) { // 獨佔鎖
fwrite($fp, $data);
fflush($fp); // 強制刷新到磁盤
flock($fp, LOCK_UN);
} else {
// 獲取鎖失敗:重試/排隊/丟棄
}
fclose($fp);
<?php
$path = 'c:/export/simple.txt';
$content = "導出時間: " . date('c') . PHP_EOL;
$content .= "GET: " . json_encode($_GET, JSON_UNESCAPED_UNICODE) . PHP_EOL;
$content .= "POST: " . json_encode($_POST, JSON_UNESCAPED_UNICODE) . PHP_EOL;
// 旗標組合:
// FILE_APPEND = 追加模式 (默認覆蓋)
// LOCK_EX = 寫入期間獨佔鎖 (防併發)
// FILE_TEXT = 文本模式 (Windows 換行符轉換)
file_put_contents($path, $content, FILE_APPEND | LOCK_EX);
$tmp = $path . '.tmp.' . uniqid();
file_put_contents($tmp, $content, LOCK_EX);
rename($tmp, $path); // rename 在同一文件系統原子性
| 維度 | fopen/fwrite | file_put_contents |
|---|---|---|
| 內存佔用 | O(1) 流式,適合 GB 級大文件 | O(N) 需將完整字符串載入內存 |
| 寫入控制 | 精細:分塊、定位、鎖、刷新 | 粗粒度:整體寫入、追加、鎖 |
| 代碼簡潔度 | 4-6 行 | 1 行 |
| 原子性支持 | 手動實現 (tmp+rename) | 手動實現 (tmp+rename) |
| 適用場景 | 大日志、流式導出、長時間運行進程 | 配置生成、小報表、調試存檔、緩存文件 |
is_writable(dirname($path)) 確保目錄可寫。/var/www/data/、/tmp/、/data/export/ 等非公開目錄。basename() + 白名單字符 + random_bytes() 前綴,防目錄遍歷。find /data/export -mtime +30 -delete。file_put_contents($path, $data, FILE_APPEND | LOCK_EX) 最簡潔安全。fopen('a') + flock + 分塊 fwrite。rename 實現原子替換,讀端永遠見完整文件。選對工具,讓文件導出既簡單又可靠。