PHP流式输出代码实现

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