指尖上的记忆指尖上的记忆
首页
  • 基础
  • Laravel框架
  • Symfony框架
  • 基础
  • Gin框架
  • 基础
  • Spring框架
  • 命令
  • Nginx
  • Ai
  • Deploy
  • Docker
  • K8s
  • Micro
  • RabbitMQ
  • Mysql
  • PostgreSsql
  • Redis
  • MongoDb
  • Html
  • Js
  • 前端
  • 后端
  • Git
  • 知识扫盲
  • Golang
🌟 gitHub
首页
  • 基础
  • Laravel框架
  • Symfony框架
  • 基础
  • Gin框架
  • 基础
  • Spring框架
  • 命令
  • Nginx
  • Ai
  • Deploy
  • Docker
  • K8s
  • Micro
  • RabbitMQ
  • Mysql
  • PostgreSsql
  • Redis
  • MongoDb
  • Html
  • Js
  • 前端
  • 后端
  • Git
  • 知识扫盲
  • Golang
🌟 gitHub
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. 工作原理
  1. 应用层写入
  • 代码执行:

    file_put_contents('php://output', "Hello World");
    

    或者库中:

    $writer->save('php://output');
    

    都会将数据写入 php://output 流。

  1. 进入输出缓冲区(可选)
  • 如果开启了 ob_start(),数据会先进入缓冲区。
  • 如果没开启缓冲区,数据会直接进入 PHP 的输出处理阶段。
  1. SAPI(Server API)阶段
  • PHP 会通过对应的 SAPI(如 Apache mod_php、FPM、CLI Server)把数据交给 Web 服务器或直接发给客户端。
  1. 客户端接收
  • 最终数据随 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。
    
  • 流式响应大文件(避免一次性加载到内存)

  • 与第三方库对接(很多库支持把生成的数据写到流,而不需要文件路径)