ThinkPHP导入Excel内存耗尽需五步优化:一用$file->getRealPath()+fopen流式加载防路径失效;二设setReadDataOnly(true)和setLoadSheetsOnly(['Sheet1'])禁样式与多余表;三用getRowIterator()+getCellIterator()逐行遍历并手动unset释放;四精准转换日期数字类型,过滤假空行;五启用APCu缓存降内存80%。

ThinkPHP导入Excel时PHP进程内存耗尽、脚本直接崩溃,不是代码写错了,而是PhpSpreadsheet在加载xlsx文件时默认执行解压+DOM解析+样式重建+公式预计算四步操作,一个10万行带格式的表轻松吃掉512MB内存。
第一步:改用真实路径+流式加载
上传后立刻调用$file->getRealPath()获取临时文件真实路径,别用$file->getPathname()——它返回的是客户端原始路径(如C:\Users\张三\数据.xlsx),服务端根本打不开。
用fopen($file->getRealPath(), 'r')包一层再传给IOFactory::load(),能绕过PHP 8+对流处理的严格校验,避免“Could not open for reading! File does not exist”错误。
【必须在upload完成后的第一行就取getRealPath()】,ThinkPHP可能在后续流程中自动清理/tmp下的临时文件,延迟调用会导致文件已不存在。
立即学习“PHP免费学习笔记(深入)”;
第二步:禁用样式与限定工作表
调用IOFactory::load()后立即执行$reader->setReadDataOnly(true),跳过字体、边框、颜色等全部样式解析。
如果Excel只有一张有效数据表,加一句$reader->setLoadSheetsOnly(['Sheet1']),防止自动加载隐藏sheet或图表页——这些页面哪怕为空,也会触发全量解析。
别信“设了setReadDataOnly(true)就安全”,这个方法必须在load()之后、getActiveSheet()之前调用,顺序错一步,样式照样进内存。
第三步:逐行迭代,手动释放对象
第一步:获取活动工作表:$worksheet = $reader->getActiveSheet();
第二步:启用行迭代器,从第2行开始(跳过表头),每500行为一块:$rowIterator = $worksheet->getRowIterator(2, 500);
第三步:遍历每行时,开启单元格迭代并跳过空白:$cellIterator = $row->getCellIterator(); $cellIterator->setIterateOnlyExistingCells(true);
第四步:处理完单行后,立刻unset($row); unset($cell);——PHP 8.3对PhpOffice\PhpSpreadsheet\Worksheet\Row类释放严重滞后,不手动清会持续累积内存。
第五步:每处理完1000行,强制回收垃圾:gc_collect_cycles();,防止引用计数残留拖垮GC。
第四步:日期与数字字段精准转换
方法一:判断是否为数字型单元格:$cell->getDataType() === \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC,再结合样式格式码识别日期。
方法二:对疑似日期字段,先取格式码:$formatCode = $cell->getStyle()->getNumberFormat()->getFormatCode();,若匹配'yyyy-mm-dd'或'yyyy/m/d',再用\PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject($cell->getValue())转为DateTime对象。
方法三:数值列不能只靠is_numeric()判断,Excel里“123456789012”会被自动转成1.23456789012E+11,必须用$cell->getFormattedValue()取显示值,否则入库就是科学计数乱码。
注意:空单元格可能返回null、''或0,清洗前先做empty(array_filter($values, 'trim'))过滤含空格的“假空行”。
第五步:启用APCu缓存(可选但强烈推荐)
执行composer require cache/simple-cache-bridge cache/apcu-adapter安装依赖。
在IOFactory::load()之前插入缓存配置:
$pool = new \Cache\Adapter\Apcu\ApcuCachePool();
$simpleCache = new \Cache\Bridge\SimpleCache\SimpleCacheBridge($pool);
\PhpOffice\PhpSpreadsheet\Settings::setCache($simpleCache);
APCu缓存能将单元格对象存到共享内存,实测内存占用下降80%以上,且部署零配置。



















