本文目录导读:

在PHP项目中实现进度条效果,通常需要结合前端轮询或WebSocket技术,因为PHP本身是同步阻塞的,以下是几种常见实现方式:
基于Session + 轮询(最常用)
后端代码 (process.php)
<?php
session_start();
// 启动长时间任务
if (!isset($_GET['action'])) {
// 模拟任务
$_SESSION['progress'] = 0;
$total = 100;
for ($i = 1; $i <= $total; $i++) {
// 模拟耗时操作
usleep(50000); // 50ms
$_SESSION['progress'] = round(($i / $total) * 100);
// 如果需要停止
if (isset($_SESSION['stop']) && $_SESSION['stop']) {
$_SESSION['progress'] = -1;
break;
}
}
echo "完成";
}
// 获取进度
if (isset($_GET['action']) && $_GET['action'] == 'progress') {
header('Content-Type: application/json');
echo json_encode([
'progress' => $_SESSION['progress'] ?? 0
]);
}
?>
前端代码 (HTML + JavaScript)
<!DOCTYPE html>
<html>
<head>PHP进度条示例</title>
<style>
.progress-bar {
width: 100%;
height: 30px;
background-color: #f0f0f0;
border-radius: 5px;
margin: 20px 0;
}
.progress-fill {
height: 100%;
background-color: #4CAF50;
border-radius: 5px;
transition: width 0.3s ease;
width: 0%;
text-align: center;
line-height: 30px;
color: white;
}
</style>
</head>
<body>
<button onclick="startTask()">开始任务</button>
<div class="progress-bar">
<div class="progress-fill" id="progressFill">0%</div>
</div>
<p id="status">等待开始...</p>
<script>
let progressInterval;
function startTask() {
document.getElementById('status').textContent = '任务进行中...';
// 获取进度
progressInterval = setInterval(function() {
fetch('process.php?action=progress')
.then(response => response.json())
.then(data => {
const progress = data.progress;
if (progress >= 0 && progress <= 100) {
updateProgress(progress);
}
// 任务完成
if (progress >= 100) {
clearInterval(progressInterval);
document.getElementById('status').textContent = '任务完成!';
}
// 任务被取消
if (progress == -1) {
clearInterval(progressInterval);
document.getElementById('status').textContent = '任务已取消';
}
});
}, 500); // 每500ms查询一次
// 启动后台任务
fetch('process.php');
}
function updateProgress(value) {
const fill = document.getElementById('progressFill');
fill.style.width = value + '%';
fill.textContent = value + '%';
}
</script>
</body>
</html>
使用文件存储进度(适用于多用户)
后端代码
<?php
function getProgressFile($taskId) {
return "/tmp/progress_" . md5($taskId) . ".txt";
}
// 创建任务
if (!isset($_GET['action'])) {
$taskId = $_GET['task_id'] ?? uniqid();
$progressFile = getProgressFile($taskId);
// 模拟文件处理
file_put_contents($progressFile, '0');
$total = 100;
for ($i = 1; $i <= $total; $i++) {
usleep(50000);
$progress = round(($i / $total) * 100);
file_put_contents($progressFile, $progress);
}
file_put_contents($progressFile, '100');
echo json_encode(['task_id' => $taskId]);
}
// 获取进度
if (isset($_GET['action']) && $_GET['action'] == 'progress') {
$taskId = $_GET['task_id'] ?? '';
$progressFile = getProgressFile($taskId);
if (file_exists($progressFile)) {
header('Content-Type: application/json');
echo json_encode(['progress' => file_get_contents($progressFile)]);
}
}
?>
使用数据库存储进度
<?php
// 数据库表结构
// CREATE TABLE task_progress (
// id INT AUTO_INCREMENT PRIMARY KEY,
// task_id VARCHAR(64) UNIQUE,
// progress INT DEFAULT 0,
// status VARCHAR(20) DEFAULT 'running',
// created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
// );
// 更新进度
function updateProgress($taskId, $progress, $status = 'running') {
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
$stmt = $db->prepare("UPDATE task_progress SET progress = ?, status = ? WHERE task_id = ?");
$stmt->execute([$progress, $status, $taskId]);
}
// 获取进度
function getProgress($taskId) {
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
$stmt = $db->prepare("SELECT progress, status FROM task_progress WHERE task_id = ?");
$stmt->execute([$taskId]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
?>
使用WebSocket(更实时)
使用 Ratchet 或 Swoole 实现:
// 需要安装 Ratchet
// composer require cboden/ratchet
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class ProgressWebSocket implements MessageComponentInterface {
protected $clients;
protected $progress = 0;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
// 客户端发送 "start" 开始任务
if ($msg === 'start') {
for ($i = 1; $i <= 100; $i++) {
usleep(50000);
$this->progress = $i;
// 广播进度
foreach ($this->clients as $client) {
$client->send($this->progress);
}
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e) {
$conn->close();
}
}
使用现代前端技术(Fetch + AbortController)
// 支持取消任务的版本
async function startTaskWithCancel() {
const controller = new AbortController();
try {
const response = await fetch('process.php', {
signal: controller.signal
});
// 轮询进度
const progressInterval = setInterval(async () => {
const progressResponse = await fetch('progress.php');
const data = await progressResponse.json();
if (data.progress >= 100) {
clearInterval(progressInterval);
}
updateProgress(data.progress);
}, 200);
} catch (err) {
if (err.name === 'AbortError') {
console.log('任务已取消');
}
}
}
// 取消任务
function cancelTask() {
controller.abort();
fetch('cancel.php'); // 通知后端取消
}
最佳实践建议
-
选择合适的方案:
- 简单项目:Session + 轮询
- 高并发:文件或数据库存储
- 实时性要求高:WebSocket
-
性能优化:
- 设置合理的轮询间隔(200-1000ms)
- 使用缓存减少数据库查询
- 考虑使用Redis存储进度
-
用户体验:
- 显示预计剩余时间
- 支持取消操作
- 添加动画过渡效果
- 显示任务详细信息
-
安全性:
- 验证任务所有权
- 设置超时机制
- 防止重复请求
选择哪种实现方式取决于你的具体需求、服务器环境以及项目复杂度,对于大多数简单的PHP项目,Session + 轮询 是最简单直接的方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播