必须用PhpSpreadsheet替代PHPExcel,安装需PHP≥7.4,引入IOFactory并显式创建Xlsx/Xls Reader,校验真实MIME与扩展名,调用getRealPath()获取路径,setReadDataOnly(true)禁用样式,getRowIterator(1,10)流式读前10行,getCellIterator()->setIterateOnlyExistingCells(false)保列序,mb_convert_encoding()转UTF-8防乱码,空值转空字符串,合并单元格需单独解析。

ThinkPHP6.0项目需将用户上传的Excel文件安全读取并转为可用数组,避免内存溢出、中文乱码、空单元格错位及恶意文件执行,必须绕过$_FILES['type']伪造风险、禁用toArray()全量加载、显式控制Reader类型并强制UTF-8转码。
安装与命名空间配置
执行composer require phpoffice/phpspreadsheet安装最新稳定版;TP6.0要求PHP≥7.4,低于该版本会导致DateTime类加载失败而中断读取。在控制器顶部写use PhpOffice\PhpSpreadsheet\IOFactory;——漏掉PhpOffice\前缀必报Class not found错误,这是90%新手卡住的第一步。
验证是否生效:新建测试方法,调用IOFactory::createReader('Xlsx')能返回Reader实例即表示安装与命名空间无误;直接new IOFactory()会报错,因IOFactory无构造函数。
安全接收并校验上传文件
别信$_FILES['excel']['type'],它由浏览器伪造,完全不可靠。必须用finfo_file(finfo_open(FILEINFO_MIME_TYPE), $tmpPath)获取真实MIME类型,只允许application/vnd.openxmlformats-officedocument.spreadsheetml.sheet(xlsx)和application/vnd.ms-excel(xls)。
立即学习“PHP免费学习笔记(深入)”;
同时用pathinfo($file->getOriginalName(), PATHINFO_EXTENSION)取原始后缀,转小写后映射为Reader类型:【'xlsx' → 'Xlsx','xls' → 'Xls'】;拒绝.csv文件,phpspreadsheet默认不支持CSV读取,强行加载会直接抛出异常。
调用$file->getRealPath()获取真实可读路径——getPathname()返回的是/tmp/phpXXXXXX临时名,在Swoole或多进程环境下极易失效或被清理。
流式读取前10行生成预览数据
第一步:用$reader = IOFactory::createReader($readerType)显式创建Reader,比IOFactory::load()更稳更快。
第二步:$reader->setReadDataOnly(true)关闭样式、公式、宏解析,内存占用直降60%以上。
第三步:加载工作簿后获取首张工作表:$spreadsheet = $reader->load($path); $worksheet = $spreadsheet->getActiveSheet();
第四步:限制只读前10行,防止5万行大文件卡死:foreach ($worksheet->getRowIterator(1, 10) as $row)。注意:必须加范围参数,否则默认从第1行读到最后一行,大文件直接崩溃。
第五步:遍历单元格时强制包含空单元格:$cellIterator = $row->getCellIterator(); $cellIterator->setIterateOnlyExistingCells(false);,否则列序错位,“手机号”字段可能写进“邮箱”位置。
清洗表头与单元格值
方法一:处理中文列头乱码
读取第一行时,对每个单元格值做mb_convert_encoding($cell->getValue(), 'UTF-8', 'auto'),解决GBK编码Excel中文显示为乱码或问号的问题。
方法二:统一空单元格输出
调用$cell->getValue()后,立即用is_null()判断,空值转为空字符串:$val = is_null($cell->getValue()) ? '' : trim($cell->getValue());,避免模板中出现NULL字面量导致JSON解析失败。
方法三:应对合并单元格错位
若首行含合并单元格,getCellByColumnAndRow()可能返回空值,此时应fallback到$worksheet->getMergeCells()解析合并范围,否则表头列与实际数据列无法对齐。



















