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

PHP 實現任務執行進度百分比顯示

     編輯:PHP 相關知識

應用場景

在長時間運行的 PHP 任務中(如批量數據導入、文件處理、報表生成),實時顯示執行進度百分比能大幅提升用戶體驗。本文介紹基於 Session 的簡單進度追蹤實現。

基礎實現代碼

<?php
session_start();

// 設置任務總數(實際項目中可從數據庫或隊列獲取)
$zs = 21;

// 初始化 session 進度計數器
if (!isset($_SESSION['num'])) {
    $_SESSION['num'] = $zs;
}

// 獲取並遞減進度值
$jd = $_SESSION['num']--;

// 輸出當前進度數值
echo $jd;
echo "<br />";

// 計算並顯示完成百分比
$percentage = 100 - round($jd / $zs * 100);
echo $percentage . "%";
?>

代碼邏輯解析

  1. Session 初始化:session_start() 必須在任何輸出前調用
  2. 總量設置:$zs 代表任務總步數
  3. 進度計數:使用 $_SESSION['num'] 存儲剩餘步數,每次請求遞減
  4. 百分比計算:(總數 - 剩餘) / 總數 * 100,使用 round() 四捨五入

完整項目級實現方案

1. 服務端:長任務處理器

<?php
// task_processor.php
session_start();

class TaskProgress {
    private $taskId;
    private $totalSteps;
    private $currentStep = 0;
    
    public function __construct(string $taskId, int $totalSteps) {
        $this->taskId = $taskId;
        $this->totalSteps = $totalSteps;
        $_SESSION[$this->taskId] = [
            'total' => $totalSteps,
            'current' => 0,
            'status' => 'running',
            'start_time' => time()
        ];
    }
    
    public function increment(int $step = 1): void {
        $this->currentStep += $step;
        $_SESSION[$this->taskId]['current'] = $this->currentStep;
        
        if ($this->currentStep >= $this->totalSteps) {
            $_SESSION[$this->taskId]['status'] = 'completed';
            $_SESSION[$this->taskId]['end_time'] = time();
        }
    }
    
    public function getProgress(): array {
        return $_SESSION[$this->taskId] ?? [];
    }
    
    public function isCompleted(): bool {
        return ($_SESSION[$this->taskId]['status'] ?? '') === 'completed';
    }
}

// 模擬長任務
$task = new TaskProgress('import_users', 1000);
for ($i = 0; $i < 1000; $i++) {
    // 執行實際業務邏輯...
    usleep(10000); // 模擬耗時操作
    $task->increment();
}
?>

2. 客戶端:AJAX 輪詢進度

<!DOCTYPE html>
<html>
<head>
    <title>任務進度監控</title>
    <style>
        .progress-bar { width: 100%; height: 30px; background: #f0f0f0; border-radius: 15px; overflow: hidden; }
        .progress-fill { height: 100%; background: linear-gradient(90deg, #4caf50, #8bc34a); transition: width 0.3s; display: flex; align-items: center; justify-content: center; color: white; font-weight: bold; }
    </style>
</head>
<body>
    <div>任務進度:</div>
    <div class="progress-bar">
        <div id="progress-fill" class="progress-fill" style="width: 0%">0%</div>
    </div>
    <div id="status">準備中...</div>

    <script>
        function pollProgress() {
            fetch('get_progress.php?task=import_users')
                .then(res => res.json())
                .then(data => {
                    const pct = data.total > 0 ? Math.round(data.current / data.total * 100) : 0;
                    document.getElementById('progress-fill').style.width = pct + '%';
                    document.getElementById('progress-fill').textContent = pct + '%';
                    document.getElementById('status').textContent = `${data.current} / ${data.total} (${data.status})`;
                    
                    if (data.status !== 'completed') {
                        setTimeout(pollProgress, 500);
                    } else {
                        document.getElementById('status').textContent = '任務完成!耗時 ' + (data.end_time - data.start_time) + ' 秒';
                    }
                });
        }
        pollProgress();
    </script>
</body>
</html>

3. 進度查詢接口

<?php
// get_progress.php
session_start();
header('Content-Type: application/json');

$taskId = $_GET['task'] ?? '';
if (empty($taskId) || !isset($_SESSION[$taskId])) {
    echo json_encode(['error' => '任務不存在']);
    exit;
}

echo json_encode($_SESSION[$taskId]);
?>

進階優化策略

1. 使用 Redis 替代 Session

  • 解決 Session 文件鎖導致的並發阻塞
  • 支持分佈式部署、多服務器共享進度
  • 自動過期機制避免內存洩漏
<?php
// Redis 版本進度存儲
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

function setProgress($taskId, $current, $total, $status = 'running') {
    global $redis;
    $key = "task:progress:$taskId";
    $redis->hMSet($key, [
        'current' => $current,
        'total' => $total,
        'status' => $status,
        'updated' => time()
    ]);
    $redis->expire($key, 3600); // 1小時過期
}

function getProgress($taskId) {
    global $redis;
    $key = "task:progress:$taskId";
    return $redis->hGetAll($key);
}
?>

2. Server-Sent Events (SSE) 實時推送

替代輪詢,服務端主動推送進度更新,減少 HTTP 開銷:

<?php
// sse_progress.php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');

$taskId = $_GET['task'] ?? '';
$lastCurrent = 0;

while (true) {
    $progress = getProgress($taskId); // 使用 Redis 或 Session
    if ($progress && $progress['current'] != $lastCurrent) {
        $pct = round($progress['current'] / $progress['total'] * 100);
        echo "data: {$pct}%\n\n";
        @ob_flush(); @flush();
        $lastCurrent = $progress['current'];
    }
    
    if (($progress['status'] ?? '') === 'completed') break;
    sleep(1);
}
?>

常見問題與解決

問題原因解決方案
進度條卡在 0%Session 文件鎖阻塞使用 session_write_close() 或改用 Redis
多標籤頁干擾共用同一 Session生成唯一 Task ID,URL 參數傳遞
內存洩漏舊任務數據未清理設置 TTL 過期、定時清理任務
進度倒退並發請求競態使用原子操作(Redis INCR)或鎖機制

總結

基於 Session 的進度顯示適合簡單單機場景,生產環境建議採用 Redis + SSE/WebSocket 方案,配合唯一任務 ID 實現高併發、分佈式、實時的進度監控體驗。

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