thinkphp 6.0中excel导入导出需设simsun字体防乱码、分步处理日期、用绝对路径读临时文件、逐行迭代防内存溢出、映射中文表头、事务分批插入并及时清理文件。

在ThinkPHP 6.0项目中快速实现Excel数据导入导出,避免中文乱码、日期变数字、内存溢出或临时文件读取失败等问题,需绕过toArray()直读、禁用默认字体、显式控制单元格类型并校验上传路径。
安装与环境校验
执行命令安装PhpSpreadsheet:composer require phpoffice/phpspreadsheet
确认PHP版本不低于7.4——【低于7.4会导致DateTime处理异常且类加载失败】
检查vendor/autoload.php是否已由TP6自动引入,无需手动require
在控制器顶部声明命名空间:use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\IOFactory;
导出Excel:防乱码+日期正确显示
创建实例并获取活动工作表:$spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet();
设置全局中文字体为SimSun(宋体),不能用“黑体”“微软雅黑”等非Office内置字体,否则导出后打开提示字体替换:$spreadsheet->getDefaultStyle()->getFont()->setName('SimSun');
写入日期字段时必须分两步:先setValueExplicit设为数值类型,再应用格式码,否则Excel里显示为44562这类序列号:$cell->setValueExplicit(strtotime($date), \PhpOffice\PhpSpreadsheet\Cell\DataType::TYPE_NUMERIC); → $cell->getStyle()->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_DATE_DATETIME);
设置UTF-8响应头并URL编码文件名:header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'); header('Content-Disposition: attachment; filename="'.rawurlencode('用户报表_'.date('Y-m-d').'.xlsx').'";');
导入Excel:安全读取+大文件防护
方法一:使用临时文件绝对路径(推荐)
上传后通过$request->file('excel')获取File对象→调用move()保存到runtime/upload/目录→传入IOFactory::load()的必须是移动后的绝对路径,不能直接传$_FILES['excel']['tmp_name'],【TP6可能在后续请求中自动清理临时文件导致load失败】
方法二:流式读取防内存溢出
对超万行文件,禁用getActiveSheet()->toArray(),改用getRowIterator()逐行迭代:$reader = IOFactory::createReader('Xlsx'); $reader->setReadDataOnly(true); $spreadsheet = $reader->load($filePath); foreach ($spreadsheet->getActiveSheet()->getRowIterator() as $row) { $cellIterator = $row->getCellIterator(); $cellIterator->setIterateOnlyExistingCells(false); foreach ($cellIterator as $cell) { $value = $cell->getFormattedValue(); } }
方法三:中文表头映射清洗
若Excel首行为“用户姓名”“注册时间”,而数据库字段是name、create_time,则需构建映射数组:$headerMap = ['用户姓名' => 'name', '注册时间' => 'create_time']; → 遍历第一行获取键名 → 后续行按映射存入$data[$headerMap[$header]] = $value;
批量导入性能优化
第一步:开启事务包裹插入操作,防止中途失败残留脏数据:Db::startTrans();
第二步:每500行提交一次并触发垃圾回收:if ($i % 500 === 0) { Db::commit(); gc_collect_cycles(); }
第三步:导入完成后立即unlink($filePath),避免runtime/upload目录堆积临时文件
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











