大家好,我是专注 PHP 实战干货的博主。
做业务开发时,Excel 导入导出是高频需求,但新手常踩坑:大文件导入卡死、内存溢出、配置复杂、兼容问题。今天给大家带来PHP7+ 最简可行版,基于 PhpSpreadsheet(PHPExcel 官方替代),无需复杂配置,内置防卡死、防内存溢出优化,复制就能跑,支持大文件分批处理。
PhpSpreadsheet 是 PHPExcel 的官方升级版,兼容 PHP7+,性能更好:composer require phpoffice/phpspreadsheet "^1.29"
若没有 Composer,直接下载压缩包:https://github.com/PHPOffice/PhpSpreadsheet/releases<?php// 设置执行时间(防卡死,根据业务调整)set_time_limit(0); // 设置内存限制(防溢出,根据服务器配置调整)ini_set('memory_limit', '512M');// 禁用PhpSpreadsheet缓存(减少内存占用)define('SPREADSHEET_MEMORY_STORAGE', 'memory');
无需复杂配置,封装成单文件,包含「导出、导入、大文件分批处理」核心功能:<?php/** * PHP Excel 导入导出最简封装类(PHP7+) * 特性:防卡死、防内存溢出、极简调用、支持大文件分批 * 依赖:phpoffice/phpspreadsheet */classExcelTool{ /** * Excel 导出(支持多sheet、大文件分批) * @param array $data 导出数据 [['sheet名' => [表头[], 数据[]]]] * @param string $filename 文件名(无需后缀) * @param string $format 格式:xlsx/xls */ public static function export(array $data, string $filename = '导出数据', string $format = 'xlsx') { // 基础配置(防内存溢出) \PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod(\PhpOffice\PhpSpreadsheet\Settings::CACHE_MEMORY); $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); $spreadsheet->removeSheetByIndex(0); // 删除默认sheet // 循环处理每个sheet foreach($data as $sheetName => $sheetData) { $sheet = $spreadsheet->createSheet(); $sheet->setTitle($sheetName); // 写入表头 $header = $sheetData[0]; $col = 1; foreach($header as $h) { $sheet->setCellValueByColumnAndRow($col++, 1, $h); } // 分批写入数据(每1000行清理一次内存) $rows = $sheetData[1]; $rowCount = 2; $batchSize = 1000; $total = count($rows); for($i = 0; $i < $total; $i += $batchSize) { $batch = array_slice($rows, $i, $batchSize); foreach($batch as $row) { $col = 1; foreach($row as $cell) { $sheet->setCellValueByColumnAndRow($col++, $rowCount, $cell); } $rowCount++; } // 清理内存 gc_collect_cycles(); } } // 下载响应头 header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header("Content-Disposition: attachment;filename=\"{$filename}.{$format}\""); header('Cache-Control: max-age=0'); // 写入输出流(避免临时文件) $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, ucfirst($format)); $writer->save('php://output'); // 销毁对象,释放内存 $spreadsheet->disconnectWorksheets(); unset($spreadsheet); exit; } /** * Excel 导入(支持大文件分批读取) * @param string $filePath 文件路径 * @param int $batchSize 分批大小(默认1000行) * @param callable $callback 每批数据的处理回调 * @return array 总统计(成功行数/失败行数) */ public static function import(string $filePath, int $batchSize = 1000, callable $callback = null) { if(!file_exists($filePath)) { throw new Exception('文件不存在'); } // 基础配置 \PhpOffice\PhpSpreadsheet\Settings::setCacheStorageMethod(\PhpOffice\PhpSpreadsheet\Settings::CACHE_MEMORY); $reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile($filePath); $reader->setReadDataOnly(true); // 只读取数据,不读取格式(减少内存) $reader->setLoadSheetsOnly(true); // 只加载需要的sheet $spreadsheet = $reader->load($filePath); $stats = ['success' => 0, 'fail' => 0]; // 处理每个sheet foreach($spreadsheet->getSheetNames() as $sheetName) { $sheet = $spreadsheet->getSheetByName($sheetName); $highestRow = $sheet->getHighestRow(); $highestCol = $sheet->getHighestColumn(); $header = $sheet->rangeToArray("A1:{$highestCol}1")[0]; // 读取表头 // 分批读取数据 for($startRow = 2; $startRow <= $highestRow; $startRow += $batchSize) { $endRow = min($startRow + $batchSize - 1, $highestRow); $data = $sheet->rangeToArray("A{$startRow}:{$highestCol}{$endRow}"); // 处理当前批次 foreach($data as $row) { if(empty(array_filter($row))) continue; // 跳过空行 try { // 回调处理数据(业务逻辑) if($callback) { $callback(array_combine($header, $row)); } $stats['success']++; } catch(Exception $e) { $stats['fail']++; } } // 清理内存 gc_collect_cycles(); } } // 释放资源 $spreadsheet->disconnectWorksheets(); unset($spreadsheet); // 删除临时文件 @unlink($filePath); return $stats; }}
<?phprequire_once 'vendor/autoload.php'; // Composer自动加载require_once 'ExcelTool.php';// 导出数据(表头 + 内容)$data = [ '用户列表' => [ ['ID', '用户名', '手机号', '注册时间'], // 表头 [1, '张三', '13800138000', '2026-03-04'], [2, '李四', '13900139000', '2026-03-05'], // 可追加任意多行 ]];// 一行导出ExcelTool::export($data, '用户数据', 'xlsx');
<?phprequire_once 'vendor/autoload.php';require_once 'ExcelTool.php';// 模拟10万行大数据(实际从数据库分批查询)$header = ['ID', '商品名', '价格', '库存'];$rows = [];for($i = 1; $i <= 100000; $i++) { $rows[] = [$i, "商品{$i}", rand(10, 100), rand(100, 1000)];}$data = [ '商品列表' => [$header, $rows]];// 自动分批导出,无需额外配置ExcelTool::export($data, '十万条商品数据', 'xlsx');
<?phprequire_once 'vendor/autoload.php';require_once 'ExcelTool.php';// 上传文件处理(前端表单name="file")if($_FILES['file']['error'] === 0) { $tmpPath = $_FILES['file']['tmp_name']; // 导入并处理数据 $stats = ExcelTool::import($tmpPath, 1000, function ($row) { // 业务逻辑:比如插入数据库 echo "处理数据:" . json_encode($row) . "\n"; // 示例:INSERT INTO user (name, mobile) VALUES (?, ?) }); echo "导入完成:成功{$stats['success']}行,失败{$stats['fail']}行";}
<?phprequire_once 'vendor/autoload.php';require_once 'ExcelTool.php';// 处理50万行的大Excel$filePath = '/uploads/big_file.xlsx';// 分批导入,每1000行处理一次$stats = ExcelTool::import($filePath, 1000, function ($row) { // 批量插入数据库(推荐用PDO批量插入) static $batch = []; $batch[] = $row; // 每1000行执行一次批量插入 if(count($batch) >= 1000) { // 示例:INSERT INTO goods (name, price) VALUES (?, ?) echo "批量插入1000行\n"; $batch = []; }});echo "大文件导入完成:成功{$stats['success']}行,失败{$stats['fail']}行";
<?phprequire_once 'vendor/autoload.php';require_once 'ExcelTool.php';$data = [ '用户表' => [ ['ID', '用户名'], [1, '张三'], [2, '李四'] ], '订单表' => [ ['订单ID', '用户ID', '金额'], [1001, 1, 99], [1002, 2, 199] ]];ExcelTool::export($data, '多表数据', 'xlsx');
- 调整
ini_set('memory_limit', '1G')(根据服务器配置) - 减少
batchSize(比如从 1000 改为 500) - 开启
setReadDataOnly(true)(只读取数据,不读格式)
- 设置
set_time_limit(0)(无时间限制)
- 避免合并单元格(解析会出错,建议拆分成普通单元格)
- 表头不要有空列(否则 array_combine 会报错)
<!-- 前端上传表单 --><formmethod="post"enctype="multipart/form-data"> <inputtype="file"name="file"accept=".xlsx,.xls"> <buttontype="submit">导入</button></form>
- 内存优化
- 防卡死
- 极简调用
- 兼容性支持 PHP7+、xlsx/xls、多 sheet、大文件;
- 易扩展
关注我,后续我会继续分享:PHP 开发规范、框架实战、架构思路、运维技巧、效率工具。关注我,每天 3 分钟,提升开发效率。
欢迎在留言区告诉我:你在 Excel 导入导出中遇到过哪些坑?比如大文件处理、格式兼容、乱码等问题,我们一起交流解决方案!