使用PHP+HTML,实现流式输出效果(仿DeepSeek等对话式AI)

效果演示

后端代码

php 复制代码
<?php
// 关闭输出缓冲
ini_set('output_buffering', 'off');
ini_set('zlib.output_compression', false);
while (ob_get_level()) ob_end_clean(); // 清除所有缓冲层

// 设置HTTP头(流式内容类型 + 禁用缓存)
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');

// 模拟对话回复内容
$messages = [
    "你好!我正在分析您的问题...\n",
    "已找到相关解决方案,请稍等。\n",
    "处理完成!以下是详细回答:\n"
];

// 流式输出每条消息
foreach ($messages as $msg) {
    // 逐字输出(可选)
    // $length = strlen($msg);
    $length = mb_strlen($msg);
    for ($i=0; $i<$length; $i++) {
        // echo $msg[$i];
        $char = mb_substr($msg, $i, 1, 'UTF-8');
        echo $char;
        ob_flush(); // 刷新PHP缓冲
        flush();    // 刷新Web服务器缓冲
        usleep(50000); // 50ms延迟模拟打字效果
    }
}

// 持续生成内容的例子(如从数据库/API获取)
for ($i=1; $i<=5; $i++) {
    echo "正在处理第 {$i} 项任务...\n";
    ob_flush();
    flush();
    sleep(1);
}

前端代码

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>智能客服系统</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f4f4f9;
        }
        .chat-container {
            width: 800px;
            background-color: #fff;
            border-radius: 10px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            padding: 20px;
        }
        .messages {
            height: 500px;
            overflow-y: auto;
            border-bottom: 1px solid #ddd;
            padding-bottom: 10px;
        }
        .message {
            margin: 10px 0;
        }
        .user {
            text-align: right;
        }
        .bot {
            text-align: left;
        }
        .input-container {
            display: flex;
            margin-top: 10px;
        }
        .input-container input {
            flex: 1;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .input-container button {
            padding: 10px 20px;
            border: none;
            background-color: #007bff;
            color: #fff;
            border-radius: 5px;
            cursor: pointer;
        }
        .input-container button:hover {
            background-color: #0056b3;
        }
    </style>
</head>
<body>
<div class="chat-container">
    <div class="messages" id="messages" style="white-space: pre-wrap;"></div>
    <div class="input-container">
        <input type="text" id="userInput" placeholder="输入消息...">
        <button onclick="sendMessage()">发送</button>
    </div>
</div>

<script>
    function sendMessage() {
        const userInput = document.getElementById('userInput').value;
        if (userInput.trim() === '') return;
        document.getElementById('userInput').value = '';

        const messagesContainer = document.getElementById('messages');
        const userMessage = document.createElement('div');
        userMessage.className = 'message user';
        userMessage.textContent = userInput;
        messagesContainer.appendChild(userMessage);


        fetch('stream.php')
            .then(response => {
                const reader = response.body.getReader();
                const decoder = new TextDecoder('utf-8');

                const botMessage = document.createElement('div');
                botMessage.className = 'message bot';
                messagesContainer.appendChild(botMessage);

                function readChunk() {
                    return reader.read().then(({ done, value }) => {
                        if (done) return;
                        // 将二进制数据解码为文本
                        const text = decoder.decode(value);
                        // 实时追加到页面
                        botMessage.innerHTML += text;
                        messagesContainer.scrollTop = messagesContainer.scrollHeight;
                        // 继续读取下一块
                        return readChunk();
                    });
                }
                return readChunk();
            })
            .catch(console.error);
    }
</script>
</body>
</html>

运行测试

项目根目录下打开命令行输入以下命令,执行

bash 复制代码
php -S 127.0.0.1:6789

打开浏览器,输入 127.0.0.1:6789 访问Web界面,输入任意内容发送后,即可看到流式输出效果

原理解析

1. 什么是流式输出

流式输出通常指的是在数据生成的同时逐步发送到客户端,而不是等待所有处理完成后再一次性发送。这在实时聊天应用或需要逐步显示结果的场景中很常见。

2. PHP怎么做到流式输出

PHP默认是缓冲输出的,也就是说,脚本执行完毕后才会将内容发送到浏览器。所以需要调整输出缓冲的设置。比如调用ob_flush()和flush()来实时发送内容。

3. 前端处理数据的接收和显示

前端监听来自服务器的事件,每次接收到数据就更新页面。本次实践通过Fetch读取流式响应体,逐块处理。

相关推荐
BingoGo2 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack2 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo3 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack3 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack4 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo4 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack5 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理5 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
QQ5110082855 天前
python+springboot+django/flask的校园资料分享系统
spring boot·python·django·flask·node.js·php
WeiXin_DZbishe5 天前
基于django在线音乐数据采集的设计与实现-计算机毕设 附源码 22647
javascript·spring boot·mysql·django·node.js·php·html5