利用 PHP 串流特性實現檔案內容直接以 HTTP POST 主體傳輸,避免將大檔案完整載入記憶體,適用於微服務間檔案傳遞、異步處理管道等場景。
<?php
/**
* 以串流方式發送檔案
* @param string $url 接收端 URL
* @param string $file 要發送的本地檔案路徑
* @return bool 發送是否成功
*/
function sendStreamFile($url, $file) {
if (!file_exists($file)) {
return false;
}
// 使用串流上下文發送 POST 請求
$opts = [
'http' => [
'method' => 'POST',
'header' => 'Content-Type: application/octet-stream', // 二進位串流
'content' => fopen($file, 'rb'), // 關鍵:直接使用檔案控制代碼,不載入記憶體
'timeout' => 300 // 大檔案需較長逾時
]
];
$context = stream_context_create($opts);
// 發送請求並取得回應
$response = @file_get_contents($url, false, $context);
if ($response === false) {
return false;
}
$ret = json_decode($response, true);
return $ret['success'] ?? false;
}
// 使用範例
$result = sendStreamFile('https://localhost/receiveStreamFile.php', 'large_file.zip');
var_dump($result);
?>
<?php
/**
* 接收串流檔案並儲存
* @param string $receiveFile 儲存路徑
* @return bool
*/
function receiveStreamFile($receiveFile) {
// 讀取原始 POST 資料 (PHP 5.6+ 建議用 php://input)
$streamData = file_get_contents('php://input');
// 相容舊版:$GLOBALS['HTTP_RAW_POST_DATA'] (需 php.ini always_populate_raw_post_data=On)
if (empty($streamData) && isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
$streamData = $GLOBALS['HTTP_RAW_POST_DATA'];
}
if (empty($streamData)) {
return false;
}
// 直接寫入檔案 (串流寫入,低記憶體佔用)
$bytesWritten = @file_put_contents($receiveFile, $streamData);
return $bytesWritten !== false;
}
// 執行接收並回應 JSON
$receiveFile = 'received_' . date('Ymd_His') . '.dat';
$success = receiveStreamFile($receiveFile);
header('Content-Type: application/json');
echo json_encode(['success' => (bool)$success, 'file' => $receiveFile]);
?>
| 技術點 | 說明 | 優勢 |
|---|---|---|
| php://input | 唯讀串流,直接讀取原始 POST 主體 | 不依賴 php.ini 設定、效能優於 $HTTP_RAW_POST_DATA |
| fopen() 作為 content | 發送端直接傳入檔案控制代碼 | 零拷貝,不佔用 PHP 記憶體緩衝區 |
| Content-Type: application/octet-stream | 標準二進位串流 MIME 類型 | 避免表單解析開銷,支援任意二進位檔案 |
| file_put_contents 串流寫入 | 單次寫入或配合 FILE_APPEND 分塊 | 簡單高效,大檔案建議用 fopen/fwrite 循環 |
// 發送端:分塊讀取 (需搭配支援 Chunked Transfer Encoding 的接收端)
$opts['http']['content'] = fopen($file, 'rb');
// 確保啟用 chunked transfer
$opts['http']['header'] = "Content-Type: application/octet-stream\r\nTransfer-Encoding: chunked";
// 接收端:分塊寫入避免記憶體峰值
$input = fopen('php://input', 'rb');
$output = fopen($receiveFile, 'wb');
stream_copy_to_stream($input, $output); // 內核級複製,最高效
fclose($input);
fclose($output);
client_max_body_size、PHP post_max_sizefinfo_file() 檢測真實 MIME 類型