本文目录导读:

- 基本流式响应
- 使用 Laravel 封装的
response()->stream() - 大文件下载示例
- 实时数据流(SSE - Server Sent Events)
- 数据库大量数据导出
- 后端代码配合前端接收
- 注意事项
在 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();
});
注意事项
-
缓存问题:如果使用 Nginx,需要禁用缓冲区:
proxy_buffering off; -
PHP 配置:确保设置合适的
max_execution_time -
输出缓冲:使用
ob_flush()和flush()确保数据及时发送 -
浏览器兼容性:部分浏览器可能需要特定的响应头
-
内存管理:流式响应的主要优势就是内存友好,避免加载大量数据
流式响应非常适合处理大文件下载、实时数据更新、日志流等场景。