首先,我们先了解导出大数据量到Excel时可能遇到的问题。当我们试图一次性从数据库中取出大量数据(如5万条记录)并直接导出到Excel时,可能会因为数据处理量大而导致脚本执行时间过长,进而触发PHP的最大执行时间限制,出现超时错误。
为了解决这个问题,我们可以采用以下策略:
- 分批处理:不要一次性从数据库中取出所有数据,而是分批取出,每批处理一定数量的记录。
- 直接输出:在取数据的同时,直接将数据写入Excel文件并输出,而不是先全部存入内存再输出。
- 关闭输出缓冲:关闭PHP的输出缓冲,确保数据实时输出到浏览器。
现在,我们使用PHPExcel或PhpSpreadsheet(PHPExcel的继任者)这样的库来生成Excel文件。以下是使用PhpSpreadsheet进行大数据量导出的简单示例:
<?php require 'vendor/autoload.php'; // 引入PhpSpreadsheet库 use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); // 设置表头 $sheet->setCellValue('A1', 'ID'); $sheet->setCellValue('B1', 'Name'); $sheet->setCellValue('C1', 'Email'); // ... 其他表头 $row = 2; // 从第二行开始写入数据 $batchSize = 1000; // 每次从数据库取出的记录数 $offset = 0; // 偏移量,用于分批取出数据 // 连接数据库(这里以PDO为例) $pdo = new PDO('mysql:host=localhost;dbname=your_database', 'username', 'password'); while (true) { $stmt = $pdo->prepare("SELECT * FROM your_table LIMIT :offset, :batchSize"); $stmt->bindParam(':offset', $offset, PDO::PARAM_INT); $stmt->bindParam(':batchSize', $batchSize, PDO::PARAM_INT); $stmt->execute(); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); if (empty($results)) { break; // 如果没有更多数据,则退出循环 } foreach ($results as $result) { $sheet->setCellValue('A' . $row, $result['id']); $sheet->setCellValue('B' . $row, $result['name']); $sheet->setCellValue('C' . $row, $result['email']); // ... 设置其他单元格的值 $row++; } $offset += $batchSize; // 更新偏移量,以便下次取出下一批数据 } // 设置HTTP响应头,以便浏览器下载文件而不是显示内容 header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="data.xlsx"'); header('Cache-Control: max-age=0'); $writer = new Xlsx($spreadsheet); $writer->save('php://output'); // 直接输出到浏览器,不保存到服务器磁盘上 ?>底层原理:
- 分批处理:我们使用了
LIMIT和OFFSET在SQL查询中来分批获取数据,这样不会一次性加载所有数据到内存中。 - 直接输出:通过
php://output,我们直接将Excel文件的内容输出到浏览器,而不需要先将其保存为文件再提供下载。这减少了服务器的磁盘I/O操作。 - HTTP响应头:通过设置响应头,我们告诉浏览器这是一个文件下载请求,而不是一个普通的网页请求。这样,浏览器会提示用户保存文件,而不是在浏览器中显示文件内容。