Laravel流式响应怎么用

wen PHP项目 4

本文目录导读:

Laravel流式响应怎么用

  1. 基本流式响应
  2. 使用 Laravel 封装的 response()->stream()
  3. 大文件下载示例
  4. 实时数据流(SSE - Server Sent Events)
  5. 数据库大量数据导出
  6. 后端代码配合前端接收
  7. 注意事项

在 Laravel 中,流式响应(Streamed Response)主要用于处理大文件下载或实时数据推送,避免一次性加载大量数据到内存中,以下是具体用法:

基本流式响应

use Symfony\Component\HttpFoundation\StreamedResponse;
Route::get('/stream', function () {
    return new StreamedResponse(function () {
        // 逐行输出数据
        for ($i = 1; $i <= 100; $i++) {
            echo "Line $i\n";
            flush(); // 立即发送数据到客户端
            usleep(100000); // 模拟延迟
        }
    }, 200, [
        'Content-Type' => 'text/plain',
    ]);
});

使用 Laravel 封装的 response()->stream()

Route::get('/stream', function () {
    return response()->stream(function () {
        // 生成大文件内容
        $data = ['item1', 'item2', 'item3'];
        foreach ($data as $item) {
            echo json_encode($item) . "\n";
            ob_flush();
            flush();
        }
    }, 200, [
        'Content-Type' => 'application/json'
    ]);
});

大文件下载示例

Route::get('/large-file', function () {
    $filePath = storage_path('app/large-file.txt');
    return response()->stream(function () use ($filePath) {
        $handle = fopen($filePath, 'r');
        if ($handle) {
            while (!feof($handle)) {
                echo fgets($handle);
                ob_flush();
                flush();
            }
            fclose($handle);
        }
    }, 200, [
        'Content-Type' => 'text/plain',
        'Content-Disposition' => 'attachment; filename="download.txt"'
    ]);
});

实时数据流(SSE - Server Sent Events)

Route::get('/sse', function () {
    return response()->stream(function () {
        set_time_limit(0); // 取消脚本执行时间限制
        for ($i = 0; $i < 10; $i++) {
            $data = [
                'time' => now()->toDateTimeString(),
                'value' => rand(1, 100)
            ];
            echo "data: " . json_encode($data) . "\n\n";
            ob_flush();
            flush();
            sleep(2); // 每2秒发送一次数据
        }
        // 发送结束信号
        echo "event: end\ndata: stream finished\n\n";
        ob_flush();
        flush();
    }, 200, [
        'Content-Type' => 'text/event-stream',
        'Cache-Control' => 'no-cache',
        'Connection' => 'keep-alive',
        'X-Accel-Buffering' => 'no', // 禁用 Nginx 缓冲
    ]);
});

数据库大量数据导出

use App\Models\User;
Route::get('/export-users', function () {
    return response()->stream(function () {
        // 设置响应头
        echo "ID,Name,Email\n";
        // 使用游标批量处理
        User::cursor()->each(function ($user) {
            echo "{$user->id},{$user->name},{$user->email}\n";
            ob_flush();
            flush();
        });
    }, 200, [
        'Content-Type' => 'text/csv',
        'Content-Disposition' => 'attachment; filename="users.csv"'
    ]);
});

后端代码配合前端接收

前端 JavaScript 示例:

// 使用 fetch API 接收流式响应
async function fetchStream() {
    const response = await fetch('/stream');
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        const chunk = decoder.decode(value);
        console.log('Received:', chunk);
        // 更新 UI 元素
        document.getElementById('output').textContent += chunk;
    }
}
// 或者使用 EventSource 接收 SSE
const eventSource = new EventSource('/sse');
eventSource.onmessage = function(event) {
    console.log('Received:', event.data);
    document.getElementById('output').textContent += event.data + '\n';
};
eventSource.addEventListener('end', function() {
    console.log('Stream finished');
    eventSource.close();
});

注意事项

  1. 缓存问题:如果使用 Nginx,需要禁用缓冲区:

    proxy_buffering off;
  2. PHP 配置:确保设置合适的 max_execution_time

  3. 输出缓冲:使用 ob_flush()flush() 确保数据及时发送

  4. 浏览器兼容性:部分浏览器可能需要特定的响应头

  5. 内存管理:流式响应的主要优势就是内存友好,避免加载大量数据

流式响应非常适合处理大文件下载、实时数据更新、日志流等场景。

抱歉,评论功能暂时关闭!