在長時間運行的 PHP 任務(如資料採集、批量匯入、報表生成)中,即時在瀏覽器顯示進度條能大幅提升用戶體驗。本文解析基於 Output Buffering + JavaScript 的經典實現原理,提供完整可運行代碼,並討論現代替代方案。
ob_end_clean() 確保內容立即發送。<script>updateProgress(...);</script>。flush() 將緩衝區推送給瀏覽器。<?php
// 防止腳本超時
set_time_limit(0);
// 清空並關閉輸出緩衝
ob_end_clean();
// 模擬資料源
$total = 188;
$users = array_map(function($i) { return "User_{$i}"; }, range(1, $total));
// 進度條參數
$width = 500; // 總寬度(px)
$pixPerItem = $width / $total; // 每項佔寬度
$progress = 0;
// 輸出頁面骨架
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PHP 即時進度條演示</title>
<style>
body { font-family: Tahoma, sans-serif; font-size: 9pt; margin: 50px auto; }
#container { width: <?= $width + 8 ?>px; border: 1px solid #999; background: #f0f0f0; padding: 8px; }
#bar-bg { width: <?= $width ?>px; border: 1px solid navy; background: #fff; }
#progress { height: 16px; background: #ffcc66; width: 0; text-align: center; }
#status { margin-top: 8px; }
#percent { position: relative; top: -30px; text-align: center; font-weight: bold; font-size: 8pt; }
</style>
</head>
<body>
<div id="container">
<div id="bar-bg"><div id="progress"></div></div>
<div id="status"></div>
<div id="percent">0%</div>
</div>
<script>
function updateProgress(msg, width) {
document.getElementById('status').innerHTML = msg;
document.getElementById('progress').style.width = width + 'px';
document.getElementById('percent').innerHTML = Math.round(width / <?= $width ?> * 100) + '%';
}
</script>
<?php
// 關鍵:先發送骨架
flush();
// 處理循環
foreach ($users as $user) {
// 模擬耗時操作(實際替換為真實業務邏輯)
usleep(20000); // 20ms
$progress += $pixPerItem;
$currWidth = min($width, (int)$progress);
// 輸出 JS 更新進度
echo '<script>updateProgress("正在處理: ' . addslashes($user) . ' ...", ' . $currWidth . ');</script>';
flush(); // 強制推送
}
// 完成
echo '<script>updateProgress("處理完成!", ' . $width . ');</script>';
flush();
?>
</body>
</html>
ob_end_clean() 關閉 PHP 預設緩衝;若使用 Nginx + FastCGI,還需在 Nginx 配置中關閉 proxy_buffering off; 並確保 fastcgi_buffering off;,否則進度條會一次性跳至 100%。
| 方案 | 優點 | 缺點 |
|---|---|---|
| WebSocket / Server-Sent Events (SSE) | 真雙向/單向推送,低延遲,支援多任務並行 | 需額外服務,實現較複雜 |
| AJAX 輪詢 | 簡單,相容性好 | 頻繁請求增加伺服器負載 |
| 前端模擬進度 | 零後端改動,體驗尚可 | 非真實進度,僅適用於固定流程 |
| Laravel Horizon / Symfony Messenger + Redis | 企業級隊列監控,內建儀表板 | 需引入框架生態 |