PHP流式输出代码实现

      发布在:后端技术      评论:0 条评论

代码实现参考

前端代码

<!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>


相关文章
热门推荐