因phpexcel已停止维护且不兼容php8+,phpspreadsheet作为官方继任者支持流式读取、命名空间更清晰;需捕获特定异常、用getrealpath()和流式加载避免路径问题,手动迭代跳过空行,校验并转换数值与日期类型。

为什么用 PhpSpreadsheet 而不是 PHPExcel
因为 PHPExcel 已停止维护,TP6/TP8 项目里直接装它会报 Class not found 或 PHP 版本兼容问题(比如 PHP 8+ 下 fatal error)。PhpSpreadsheet 是官方继任者,命名空间、API 更清晰,且支持流式读取——这对大 Excel 文件很关键。
实操建议:
- 用
composer require phpoffice/phpspreadsheet安装,别手动拷文件 - TP6+ 默认用
think\facade\Log,但PhpSpreadsheet报错不走 TP 日志,需捕获PhpOffice\PhpSpreadsheet\Exception和InvalidArgumentException - 避免用
IOFactory::load()直接读上传的临时文件路径,它可能因权限或路径含中文失败;优先用IOFactory::createReader()+load()配合fopen流
load() 失败常见原因和绕过方法
上传后调 IOFactory::load($file_path) 报 Could not open ... for reading! File does not exist,大概率是路径没转义或临时文件已被 TP 清理。
实操建议:
- 用
request()->file('excel')拿到think\File对象后,立刻调->getRealPath(),别用->getPathname() - 若仍报错,改用流式加载:
$reader = IOFactory::createReader('Xlsx'); $spreadsheet = $reader->load(fopen($file->getRealPath(), 'r')); - 注意:.xls(Excel 2003)要用
'Xls',.xlsx 用'Xlsx',传错会抛Invalid reader
读取时如何跳过空行和表头、控制内存占用
默认 getActiveSheet()->toArray() 会把整张表(含空白行、合并单元格残留)全拉进内存,10MB Excel 可能吃掉 500MB 内存,TP 进程直接超限被 kill。
实操建议:
- 用
getRowIterator()+getCellIterator()手动遍历,边读边过滤:$worksheet = $spreadsheet->getActiveSheet(); foreach ($worksheet->getRowIterator() as $row) { $cellIterator = $row->getCellIterator(); $cellIterator->setIterateOnlyExistingCells(true); // 跳过空单元格 $values = []; foreach ($cellIterator as $cell) { $values[] = $cell->getValue(); } if (empty(array_filter($values))) continue; // 全空行跳过 // 处理非空行 } - 表头行(第1行)用
$row->getRowIndex() === 1判断,别硬写if ($i === 0) - 合并单元格内容只在左上角单元格有值,右侧/下方是 null,需用
$cell->isMergeRangeValueCell()或提前调$worksheet->mergeCells()解析
导入数据前必须做的字段校验和类型转换
PhpSpreadsheet 读出来的数字可能是 float(如 123.0)、日期是 Excel 序列号(如 44562),直接塞进数据库会丢精度或插错时间。
实操建议:
- 用
CellValueFormatter::format()或$cell->getFormattedValue()拿字符串值,再按需转:$val = $cell->getFormattedValue(); if (is_numeric($val) && strpos($val, '.') === false) { $val = (int)$val; } - 日期列必须判断
$cell->getDataType() === Cell::DATATYPE_NUMERIC,再用Coordinate::stringFromColumnIndex()+DateTime::excelToDateTimeObject() - TP 的
Db::insertAll()不校验字段类型,建议先用validate规则跑一遍数组,字段缺失或类型不符就throw new ValidateException()
真正麻烦的是 Excel 单元格格式和实际内容不一致——比如显示 “2023-01-01” 但底层存的是文本,这种只能靠业务规则兜底,没法靠库自动识别。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










