代码实现参考
前端代码
<!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>
相关文章