代码实现参考
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>流式输出测试</title>
<style>
body {
font-family: 'Microsoft YaHei', sans-serif;
max-width: 800px;
margin: 20px auto;
padding: 20px;
background-color: #f5f7fa;
}
.container {
background: white;
border-radius: 10px;
box-shadow: 0 2px 15px rgba(0,0,0,0.1);
padding: 25px;
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 25px;
}
.progress-container {
height: 25px;
background: #e0e0e0;
border-radius: 12px;
margin: 20px 0;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #3498db, #2ecc71);
border-radius: 12px;
width: 0%;
transition: width 0.5s;
}
#output {
background: #f8f9fa;
border: 1px solid #eaeaea;
border-radius: 8px;
padding: 15px;
height: 250px;
overflow-y: auto;
font-family: Consolas, monospace;
margin-top: 20px;
}
.btn-group {
display: flex;
gap: 10px;
margin: 20px 0;
justify-content: center;
}
button {
padding: 10px 25px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
transition: all 0.3s;
}
#startBtn {
background: #3498db;
color: white;
}
#startBtn:hover {
background: #2980b9;
}
#stopBtn {
background: #e74c3c;
color: white;
}
#stopBtn:hover {
background: #c0392b;
}
.status {
text-align: center;
margin: 10px 0;
color: #7f8c8d;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>小皮面板流式输出测试</h1>
<div class="btn-group">
<button id="startBtn">开始流式传输</button>
<button id="stopBtn">停止传输</button>
</div>
<div class="progress-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<div class="status">
状态: <span id="statusText">准备就绪</span>
</div>
<div id="output">等待数据流开始...</div>
</div>
<script>
let eventSource;
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const outputEl = document.getElementById('output');
const progressBar = document.getElementById('progressBar');
const statusText = document.getElementById('statusText');
// 启动SSE连接
startBtn.addEventListener('click', () => {
statusText.textContent = '正在连接服务器...';
outputEl.innerHTML = '';
progressBar.style.width = '0%';
// 关闭现有连接(如果有)
if (eventSource) {
eventSource.close();
}
// 创建新的EventSource(添加随机参数防止缓存)
const url = 'stream.php?t=' + Date.now();
eventSource = new EventSource(url);
// 消息处理
eventSource.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// 更新输出
outputEl.innerHTML += `[${data.timestamp}] ${data.message}<br>`;
// 更新进度条
progressBar.style.width = data.progress + '%';
// 自动滚动
outputEl.scrollTop = outputEl.scrollHeight;
// 更新状态
statusText.textContent = `接收数据中... (${data.progress}%)`;
} catch (e) {
outputEl.innerHTML += `解析错误: ${event.data}<br>`;
}
};
// 自定义事件处理
eventSource.addEventListener('end-of-stream', () => {
statusText.textContent = '流式传输已完成';
eventSource.close();
progressBar.style.width = '100%';
});
// 错误处理
eventSource.onerror = () => {
if (eventSource.readyState === EventSource.CLOSED) {
statusText.textContent = '连接已关闭';
} else {
statusText.textContent = '连接错误,尝试重连...';
setTimeout(() => {
startBtn.click();
}, 3000);
}
};
});
// 停止连接
stopBtn.addEventListener('click', () => {
if (eventSource) {
eventSource.close();
statusText.textContent = '已手动停止';
outputEl.innerHTML += '<br>连接已手动终止';
}
});
// 页面加载时自动开始
window.addEventListener('load', () => {
// 小皮面板可能需要手动点击开始
// startBtn.click();
});
</script>
</body>
</html>PHP实现代码
<?php
// 彻底禁用缓冲机制
while (ob_get_level() > 0) ob_end_clean();
ini_set('zlib.output_compression', 0);
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('X-Accel-Buffering: no'); // 关键:禁用Nginx代理缓冲
header('Connection: keep-alive');
// 流式输出内容
$messages = [
"正在初始化系统...",
"连接数据库服务器...",
"执行用户验证...",
"加载配置信息...",
"处理请求数据...",
"生成响应内容...",
"准备流式输出...",
"开始传输数据..."
];
foreach ($messages as $index => $message) {
// 检查客户端是否断开
if (connection_aborted()) {
exit();
}
$progress = round(($index + 1) / count($messages) * 100);
$timestamp = date('H:i:s');
$data = [
'message' => $message,
'progress' => $progress,
'timestamp' => $timestamp
];
// 输出SSE格式数据
echo "id: " . ($index + 1) . "\n";
echo "data: " . json_encode($data) . "\n\n";
// 强制刷新输出
if (ob_get_level() > 0) ob_flush();
flush();
// 模拟处理延迟
usleep(600000); // 0.6秒
}
// 结束事件
echo "event: complete\n";
echo "data: 流式传输成功完成\n\n";
ob_flush();
flush();apache环境设置找到配置文件
添加代码
<IfModule mod_fcgid.c> FcgidOutputBufferSize 0 </IfModule>
相关文章