PHP后台任务进度条实现

wen PHP项目 1

本文目录导读:

PHP后台任务进度条实现

  1. 前端HTML页面
  2. 后台PHP文件
  3. 使用说明

我来为您提供一个完整的PHP后台任务进度条实现方案。

前端HTML页面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">任务进度条</title>
    <style>
        .progress-container {
            max-width: 600px;
            margin: 100px auto;
            padding: 20px;
            background: #f5f5f5;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        .progress-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }
        .progress-title {
            font-size: 18px;
            font-weight: bold;
            color: #333;
        }
        .progress-percentage {
            font-size: 16px;
            color: #666;
            font-weight: bold;
        }
        .progress-bar {
            width: 100%;
            height: 30px;
            background: #e0e0e0;
            border-radius: 15px;
            overflow: hidden;
            position: relative;
        }
        .progress-bar-fill {
            height: 100%;
            background: linear-gradient(90deg, #4CAF50, #45a049);
            border-radius: 15px;
            transition: width 0.3s ease;
            width: 0%;
        }
        .progress-status {
            margin-top: 20px;
            padding: 10px;
            background: white;
            border-radius: 5px;
            border: 1px solid #ddd;
            font-size: 14px;
            color: #666;
            height: 40px;
            overflow: auto;
        }
        .btn-group {
            margin-top: 20px;
            text-align: center;
        }
        .btn {
            padding: 10px 30px;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
            margin: 0 10px;
            transition: all 0.3s;
        }
        .btn-start {
            background: #4CAF50;
            color: white;
        }
        .btn-start:hover {
            background: #45a049;
        }
        .btn-start:disabled {
            background: #cccccc;
            cursor: not-allowed;
        }
        .btn-cancel {
            background: #f44336;
            color: white;
        }
        .btn-cancel:hover {
            background: #da190b;
        }
    </style>
</head>
<body>
    <div class="progress-container">
        <div class="progress-header">
            <div class="progress-title">任务进度</div>
            <div class="progress-percentage" id="progressText">0%</div>
        </div>
        <div class="progress-bar">
            <div class="progress-bar-fill" id="progressBar"></div>
        </div>
        <div class="progress-status" id="statusText">
            等待任务开始...
        </div>
        <div class="btn-group">
            <button class="btn btn-start" id="startBtn" onclick="startTask()">
                开始任务
            </button>
            <button class="btn btn-cancel" id="cancelBtn" onclick="cancelTask()" style="display:none">
                取消任务
            </button>
        </div>
    </div>
    <script>
        let progressInterval = null;
        let taskId = null;
        // 开始任务
        function startTask() {
            document.getElementById('startBtn').disabled = true;
            document.getElementById('cancelBtn').style.display = 'inline-block';
            // 创建新的任务
            fetch('start_task.php', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    action: 'start',
                    task_name: 'example_task'
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    taskId = data.task_id;
                    document.getElementById('statusText').textContent = '任务开始...';
                    startProgressPolling();
                } else {
                    alert('任务启动失败: ' + data.message);
                }
            });
        }
        // 开始轮询进度
        function startProgressPolling() {
            // 清除旧的轮询
            if (progressInterval) {
                clearInterval(progressInterval);
            }
            // 每1秒轮询一次进度
            progressInterval = setInterval(() => {
                getProgress();
            }, 1000);
        }
        // 获取进度
        function getProgress() {
            if (!taskId) return;
            fetch(`get_progress.php?task_id=${taskId}`)
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    updateProgress(data.progress, data.status);
                    // 任务完成
                    if (data.progress >= 100) {
                        completeTask(data);
                    }
                } else {
                    // 任务出错或不存在
                    errorTask(data);
                }
            })
            .catch(error => {
                console.error('获取进度失败:', error);
                errorTask({message: '连接服务器失败'});
            });
        }
        // 更新进度显示
        function updateProgress(progress, status) {
            document.getElementById('progressBar').style.width = progress + '%';
            document.getElementById('progressText').textContent = progress + '%';
            document.getElementById('statusText').textContent = status;
            // 根据进度调整颜色
            if (progress >= 100) {
                document.getElementById('progressBar').style.background = '#4CAF50';
            } else if (progress >= 70) {
                document.getElementById('progressBar').style.background = '#ff9800';
            } else if (progress >= 50) {
                document.getElementById('progressBar').style.background = '#2196F3';
            }
        }
        // 完成任务
        function completeTask(data) {
            if (progressInterval) {
                clearInterval(progressInterval);
                progressInterval = null;
            }
            document.getElementById('startBtn').disabled = false;
            document.getElementById('cancelBtn').style.display = 'none';
            document.getElementById('statusText').textContent = data.message || '任务完成!';
            // 清理任务数据(可选)
            setTimeout(() => {
                fetch('cleanup_task.php', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({ task_id: taskId })
                });
            }, 5000);
        }
        // 任务出错
        function errorTask(data) {
            if (progressInterval) {
                clearInterval(progressInterval);
                progressInterval = null;
            }
            document.getElementById('startBtn').disabled = false;
            document.getElementById('cancelBtn').style.display = 'none';
            document.getElementById('statusText').textContent = '错误: ' + data.message;
        }
        // 取消任务
        function cancelTask() {
            if (!taskId) return;
            fetch('cancel_task.php', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ task_id: taskId })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    if (progressInterval) {
                        clearInterval(progressInterval);
                        progressInterval = null;
                    }
                    document.getElementById('startBtn').disabled = false;
                    document.getElementById('cancelBtn').style.display = 'none';
                    document.getElementById('statusText').textContent = '任务已取消';
                    document.getElementById('progressBar').style.width = '0%';
                    document.getElementById('progressText').textContent = '0%';
                } else {
                    alert('取消任务失败');
                }
            });
        }
    </script>
</body>
</html>

后台PHP文件

start_task.php (启动任务)

<?php
header('Content-Type: application/json');
// 关闭错误显示,避免干扰JSON输出
error_reporting(0);
ini_set('display_errors', 0);
// 启用会话
session_start();
// 修改: 实现真正的后台任务
function start_background_task($task_name) {
    // 生成唯一任务ID
    $task_id = uniqid('task_', true);
    // 创建进度文件
    $progress_file = __DIR__ . '/tasks/progress_' . $task_id . '.txt';
    if (!file_exists(dirname($progress_file))) {
        mkdir(dirname($progress_file), 0777, true);
    }
    // 初始化进度
    $initial_data = json_encode([
        'progress' => 0,
        'status' => '任务已启动',
        'start_time' => time(),
        'cancelled' => false
    ]);
    file_put_contents($progress_file, $initial_data);
    // 使用PHP CLI执行后台任务
    $script = __DIR__ . '/background_task.php';
    $php_path = PHP_BINARY; // 获取PHP解释器路径
    // 在Windows上使用start,在Linux上使用nohup
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        // Windows命令
        $command = 'start /B ' . escapeshellarg($php_path) . ' ' . 
                   escapeshellarg($script) . ' ' . 
                   escapeshellarg($task_id) . ' ' . 
                   escapeshellarg($task_name) . ' > NUL 2>&1';
        pclose(popen($command, 'r'));
    } else {
        // Linux命令
        $command = 'nohup ' . escapeshellarg($php_path) . ' ' . 
                   escapeshellarg($script) . ' ' . 
                   escapeshellarg($task_id) . ' ' . 
                   escapeshellarg($task_name) . ' > /dev/null 2>&1 &';
        exec($command);
    }
    return $task_id;
}
// 处理请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $input = json_decode(file_get_contents('php://input'), true);
    if (isset($input['action']) && $input['action'] === 'start') {
        $task_name = isset($input['task_name']) ? $input['task_name'] : 'default_task';
        // 检查是否已有任务在运行
        if (isset($_SESSION['current_task_id'])) {
            $prev_task = $_SESSION['current_task_id'];
            $progress_file = __DIR__ . '/tasks/progress_' . $prev_task . '.txt';
            if (file_exists($progress_file)) {
                $data = json_decode(file_get_contents($progress_file), true);
                if ($data && $data['progress'] < 100 && !$data['cancelled']) {
                    echo json_encode([
                        'success' => false,
                        'message' => '已有任务在运行中'
                    ]);
                    exit;
                }
            }
        }
        // 启动新的后台任务
        $task_id = start_background_task($task_name);
        // 保存当前任务ID到会话
        $_SESSION['current_task_id'] = $task_id;
        echo json_encode([
            'success' => true,
            'task_id' => $task_id,
            'message' => '任务启动成功'
        ]);
    } else {
        echo json_encode([
            'success' => false,
            'message' => '无效的操作'
        ]);
    }
} else {
    echo json_encode([
        'success' => false,
        'message' => '无效的请求方法'
    ]);
}
?>

background_task.php (后台任务处理)

<?php
// 后台任务脚本
error_reporting(E_ALL);
ini_set('display_errors', 0);
set_time_limit(0); // 不限制执行时间
// 获取参数
$task_id = isset($argv[1]) ? $argv[1] : '';
$task_name = isset($argv[2]) ? $argv[2] : 'default_task';
if (empty($task_id)) {
    exit('Invalid task ID');
}
// 进度文件路径
$progress_file = __DIR__ . '/tasks/progress_' . $task_id . '.txt';
// 模拟长时间任务(实际应用中这里会是真实的业务逻辑)
function perform_task($task_id, $task_name, $progress_file) {
    // 模拟任务总步骤
    $total_steps = 100;
    for ($step = 1; $step <= $total_steps; $step++) {
        // 检查是否取消
        if (file_exists($progress_file)) {
            $data = json_decode(file_get_contents($progress_file), true);
            if ($data && isset($data['cancelled']) && $data['cancelled']) {
                update_progress($progress_file, $step, '任务已取消', true);
                return 'cancelled';
            }
        }
        // 模拟工作
        sleep(1); // 1秒处理一步,总共约100秒
        // 更新进度
        $progress = round(($step / $total_steps) * 100);
        $status = "正在处理第 {$step} 步 / 共 {$total_steps} 步";
        update_progress($progress_file, $progress, $status);
        // 随机模拟一些错误情况(可选)
        if ($step == 50 && rand(0, 100) < 5) { // 5%的概率在第50步出错
            update_progress($progress_file, 50, '任务出错:模拟错误', true);
            return 'error';
        }
    }
    // 任务完成
    update_progress($progress_file, 100, '任务完成!');
    return 'completed';
}
// 更新进度
function update_progress($file, $progress, $status, $is_error = false) {
    $data = [
        'progress' => $progress,
        'status' => $status,
        'last_update' => time(),
        'cancelled' => isset($is_error) && $is_error ? true : false,
        'completed' => $progress >= 100 || ($is_error && $progress < 100)
    ];
    if ($progress >= 100) {
        $data['completed'] = true;
        $data['message'] = '任务完成!';
    }
    file_put_contents($file, json_encode($data));
}
// 执行任务
$result = perform_task($task_id, $task_name, $progress_file);
// 记录任务结果
if ($result === 'completed') {
    echo "Task completed successfully\n";
} else if ($result === 'cancelled') {
    echo "Task cancelled\n";
} else {
    echo "Task failed\n";
}
?>

get_progress.php (获取进度)

<?php
header('Content-Type: application/json');
session_start();
error_reporting(0);
ini_set('display_errors', 0);
// 获取任务ID
$task_id = isset($_GET['task_id']) ? $_GET['task_id'] : '';
if (empty($task_id)) {
    echo json_encode(['success' => false, 'message' => '任务ID无效']);
    exit;
}
// 读取进度文件
$progress_file = __DIR__ . '/tasks/progress_' . $task_id . '.txt';
if (!file_exists($progress_file)) {
    echo json_encode([
        'success' => false,
        'message' => '任务不存在或已过期'
    ]);
    exit;
}
$data = json_decode(file_get_contents($progress_file), true);
if (!$data) {
    echo json_encode([
        'success' => false,
        'message' => '无法读取任务进度'
    ]);
    exit;
}
// 返回进度信息
echo json_encode([
    'success' => true,
    'progress' => $data['progress'],
    'status' => $data['status'],
    'message' => isset($data['message']) ? $data['message'] : ''
]);
?>

cancel_task.php (取消任务)

<?php
header('Content-Type: application/json');
session_start();
error_reporting(0);
ini_set('display_errors', 0);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $input = json_decode(file_get_contents('php://input'), true);
    $task_id = isset($input['task_id']) ? $input['task_id'] : '';
    if (empty($task_id)) {
        echo json_encode(['success' => false, 'message' => '任务ID无效']);
        exit;
    }
    // 更新任务状态为取消
    $progress_file = __DIR__ . '/tasks/progress_' . $task_id . '.txt';
    if (file_exists($progress_file)) {
        $data = json_decode(file_get_contents($progress_file), true);
        if ($data) {
            $data['cancelled'] = true;
            $data['status'] = '任务取消中...';
            file_put_contents($progress_file, json_encode($data));
            echo json_encode([
                'success' => true,
                'message' => '任务取消已触发'
            ]);
        } else {
            echo json_encode(['success' => false, 'message' => '任务数据无效']);
        }
    } else {
        echo json_encode(['success' => false, 'message' => '任务不存在']);
    }
} else {
    echo json_encode(['success' => false, 'message' => '无效的请求方法']);
}
?>

cleanup_task.php (清理任务)

<?php
header('Content-Type: application/json');
error_reporting(0);
ini_set('display_errors', 0);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $input = json_decode(file_get_contents('php://input'), true);
    $task_id = isset($input['task_id']) ? $input['task_id'] : '';
    if (!empty($task_id)) {
        // 删除进度文件
        $progress_file = __DIR__ . '/tasks/progress_' . $task_id . '.txt';
        if (file_exists($progress_file)) {
            unlink($progress_file);
            // 可选的:删除其他相关文件
            $log_file = __DIR__ . '/tasks/log_' . $task_id . '.txt';
            if (file_exists($log_file)) {
                unlink($log_file);
            }
            echo json_encode(['success' => true, 'message' => '任务数据已清理']);
        } else {
            echo json_encode(['success' => false, 'message' => '任务文件不存在']);
        }
    } else {
        echo json_encode(['success' => false, 'message' => '任务ID无效']);
    }
} else {
    echo json_encode(['success' => false, 'message' => '无效的请求方法']);
}
?>

使用说明

  1. 创建目录结构

    mkdir -p /path/to/project/tasks
    chmod 755 /path/to/project/tasks
  2. 部署文件: 将所有PHP文件放在同一目录下,确保tasks目录有正确权限。

  3. 安全建议

  • 添加身份验证
  • 限制任务并发数
  • 设置任务超时
  • 清理过期任务文件
  • 使用HTTPS传输
  1. 功能特点
  • ✅ 真正的后台任务执行
  • ✅ 实时进度更新
  • ✅ 任务取消功能
  • ✅ 错误处理
  • ✅ 跨平台兼容(Windows/Linux)

这个实现提供了一个完整的后台任务进度条方案,可以根据实际需求进行修改和扩展。

抱歉,评论功能暂时关闭!