php导出excel的几种方式
- 方式1 通过API返回的方式
ob_start(); try { $writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); $writer->save('php://output'); $data = ob_get_clean(); if (false === $data) { throw new ExportFailedException(); } } catch (\Throwable $throwable) { ob_end_clean(); throw new ExportFailedException(); } return new ExportResponseDto(base64_encode($data), $filename . '.xlsx');代码结构分析
1.开启输出缓冲区。 2.让 $writer 将 Excel 文件内容写到 php://output(实际上进入缓冲区)。 3.从缓冲区读取数据并清空。 4.如果出错则抛出异常。 5.将文件数据 base64 编码,并和文件名一起封装到 DTO 里返回。使用场景
1.API 导出文件,因为直接传二进制文件在 JSON 中不方便,所以会先 base64 编码。 2.前端拿到数据后,可以 atob() 解码生成 Blob,然后触发下载。 3.避免直接写临时文件到磁盘,提高性能。
- 方式2 通过浏览器直接返回
<?php namespace App\Controller; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Spreadsheet; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class ExportController extends AbstractController { #[Route('/export', name: 'app_export_excel')] public function export(): Response { // 1. 创建 Excel 对象 $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); $sheet->setCellValue('A1', 'Hello World!'); $sheet->setCellValue('B1', date('Y-m-d H:i:s')); // 2. 创建 Writer $writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); // 3. 输出头部,告诉浏览器是 Excel 文件 $filename = 'export_' . date('Ymd_His') . '.xlsx'; header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="' . $filename . '"'); header('Cache-Control: max-age=0'); header('Expires: 0'); header('Pragma: public'); // 4. 保存到 php://output(直接输出给浏览器) $writer->save('php://output'); exit; // 终止脚本 } }代码结构分析
1.不使用 ob_start() 因为我们不需要获取二进制数据进行二次处理,而是直接输出给浏览器。 2.设置正确的 HTTP 头 让浏览器识别为 Excel 文件并自动下载: Content-Type 指定 MIME 类型为 .xlsx Content-Disposition 指定下载文件名 Cache-Control / Expires / Pragma 用于防止缓存 3.php://output 的作用 它是一个特殊的写入流,写进去的内容会直接发到 HTTP 响应。 4.exit 结束执行 防止 Symfony 在后面再写额外的 HTML 或 JSON 内容破坏文件。
php://output 原理
php://output 其实是 PHP 提供的一个 特殊的只写(write-only)流封装器,它的核心作用是:
让你用文件流的方式,直接把数据写到 PHP 的 HTTP 响应输出(也就是发送给客户端的内容)。
1. 它是什么
php://output是 php:// 封装器 系列的一员。- 类型是 只写流(write-only stream)。
- 它直接连接到 PHP 的 输出缓冲机制(Output Buffering System)。
- 写入
php://output的数据 等效于 执行echo、print或printf。
2. 工作原理
- 应用层写入
代码执行:
file_put_contents('php://output', "Hello World");或者库中:
$writer->save('php://output');都会将数据写入
php://output流。
- 进入输出缓冲区(可选)
- 如果开启了
ob_start(),数据会先进入缓冲区。 - 如果没开启缓冲区,数据会直接进入 PHP 的输出处理阶段。
- SAPI(Server API)阶段
- PHP 会通过对应的 SAPI(如 Apache mod_php、FPM、CLI Server)把数据交给 Web 服务器或直接发给客户端。
- 客户端接收
- 最终数据随 HTTP 响应一起发给浏览器、curl、Postman 等客户端。
3. 关键特性
无文件落地 不会在磁盘上生成临时文件,直接走内存和输出通道。
可以配合任何支持写入流的 API 比如:
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); $writer->save('php://output');这时,Excel 数据直接进 HTTP 响应,不用
echo一堆二进制。受输出缓冲区影响
- 如果
ob_start()已经打开,php://output会把数据先写入缓冲区,只有ob_flush()/ob_end_flush()才会真正发送。 - 如果没开启缓冲区,直接写到客户端。
- 如果
4. 对比其他 php:// 流
| 封装器 | 用途 |
|---|---|
php://input | 只读,读取原始 HTTP 请求体(比如 JSON、XML、文件上传的原始流) |
php://output | 只写,直接写到 HTTP 响应 |
php://memory | 读写内存中的数据(可随机访问) |
php://temp | 像 memory,但数据超过限制会写到临时文件 |
5. 典型应用
文件导出(Excel、CSV、PDF 等)
header('Content-Type: text/csv'); header('Content-Disposition: attachment;filename="data.csv"'); $fp = fopen('php://output', 'w'); fputcsv($fp, ['id', 'name']); fputcsv($fp, [1, 'Alice']); fclose($fp);file_put_contents('php://output', "Hello World"); 等价于 echo "Hello World"; 简单分析: 1.file_put_contents 调用: PHP 看到路径是 php://output,就不会去找磁盘文件,而是使用 php:// 封装器 里的 output 模块。 2.进入输出通道: 写入的数据会交给 PHP 的输出系统(Output Handler)。 3.可能先进入输出缓冲区(如果开启了 ob_start()): 如果有输出缓冲,那么数据先暂存在内存,直到你 ob_flush() 或 ob_end_flush()。 如果没开启缓冲,直接进入下一步。 4.交给 SAPI(Server API)层: PHP 把数据交给运行环境: FPM 模式:交给 PHP-FPM,再发给 Nginx/Apache CLI 模式:直接输出到终端 内置服务器:直接发给浏览器 5.客户端接收并显示 浏览器、curl、Postman 等收到 Hello World。流式响应大文件(避免一次性加载到内存)
与第三方库对接(很多库支持把生成的数据写到流,而不需要文件路径)
